Laravel Volt 实时状态
我们来创建一个 Volt 组件,用它来显示实时用户数量。
概念
要实现该特性,我们需要一个方法来跟踪活跃用户。一个方法是,将活跃用户以日志记入数据库表格。每个条目包含用户 ID 以及何时激活的时间戳。然后,您可以检查最后 5 分钟内或任何其他所需时间段内的活动。另一种方法是在 users 表中添加一个 “last_login_at” 字段。第三种可能性是使用 websockets 服务器来获得活动用户的实时计数。
本文中,我将使用 Redis。Redis 是一个简单的内存数据库,功能强大。我以前用过此方法,它能够扩展到处理数千甚至数百万用户。
首先,我们将创建一个中间件。对于每个 web 请求,该中间件都会为当前用户设置或更新一个 Redis 密钥,该密钥将在 5 分钟后过期。您可以根据需要调整此持续时间。
接下来,我们将使用 Livewire volt 组件,该组件每 5 秒刷新一次,并统计 Redis 中的密钥数量。
按照这些步骤,我们可以很容易地跟踪我们网站上的活跃用户数量。
准备 Volt
首先,需要安装 Livewire 3 和 Volt。
composer require livewire/livewire "^3.0" # Make sure you have Livewire v3.x installed...
composer require livewire/volt "^1.0"
完成以上步骤后,运行如下命令,安装好 Volt:
php artisan volt:install
完成后,我们将着手构建我们的功能。
注意:我假设您已经有一个 Laravel 应用,数据库中有一个 users 表,并且已经配置了 Redis。
使用中间件跟踪用户状态
为了跟踪用户活动,我们将创建一个中间件,每当 Redis 加载网页时,该中间件会将每个唯一用户的活动记录到 Redis。
首先,生成中间件:
php artisan make:middleware LogUserActivity
然后,我们编辑 LogUserActivity
中间件:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use Symfony\Component\HttpFoundation\Response;
class LogUserActivity
{
public function handle(Request $request, Closure $next): Response
{
if ($request->user()) {
$userId = $request->user()->id;
$duration = 300; // This represents 5 minutes
Redis::setex("live_users:$userId", $duration, 1);
}
return $next($request);
}
}
在这个中间件中,我们检查是否有授权用户。如果是这样,我们在 Redis 中使用 “live_users” 前缀存储一个 key, 其后是用户 ID。
最后,记住将该中间件添加到 Kernel.php 文件,以将其激活。
namespace App\Http;
class Kernel extends HttpKernel
{
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
LogUserActivity::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
}
为实时活跃用户创建 Volt 组件
我们来创建 Volt 组件,显示实时活跃用户数量。
php artisan make:volt liveusers
该命令在 resources/views/livewire/
内生成一个名为 liveusers.blade.php
的文件。
为了阐明这些步骤,我将把代码解释分解为两部分:PHP 代码和 HTML 内容。
Volt 内的 PHP 代码
让我们从 Volt 组件的初始化开始:
use function Livewire\Volt\{computed};
use Illuminate\Support\Facades\Redis;
$liveUsers = computed(function () {
$count = 0;
$cursor = null;
$pattern = 'live_users:*';
$batchSize = 1000;
do {
list($cursor, $keys) = Redis::scan($cursor, $pattern, $batchSize);
$count += count($keys ?? []);
} while ($cursor != 0);
return $count;
});
首先,我们引入必要的函数。computed 函数与 Redis Facade一起从 Livewire\Volt
命名空间引入。
然后,我们使用 computed
函数来获取活动用户数。选择这种方法而不是通常的 state
方法,因为它允许在不重新加载整页的情况下通过长轮询来更新值。
在 computed 回调中,我们使用 scan 方法,这是一种更有效的方法来检索所有前缀为 “live_users:” 的键。在处理大型数据集时,这种方法比 keys 方法更受欢迎,因为它允许我们在不过载内存的情况下迭代大量的项。
HTML 内容
以下是该组件的 HTML 内容:
<div>
<div wire:poll.5s>
<h3 class="text-base font-semibold leading-6 text-gray-900">Live stats</h3>
<dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div class="relative overflow-hidden rounded-lg bg-white px-4 pb-12 pt-5 shadow sm:px-6 sm:pt-6">
<dt>
<div class="absolute rounded-md bg-indigo-500 p-3">
<svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z"/>
</svg>
</div>
<p class="ml-16 truncate text-sm font-medium text-gray-500">Live Now</p>
</dt>
<dd class="ml-16 flex items-baseline">
<p class="text-2xl font-semibold text-gray-900">{{ number_format($this->liveUsers, 0) }}</p>
</dd>
</div>
</dl>
</div>
</div>
请注意第 2 行中有 wire:poll.5s
,用来每 5 秒轮询数据刷新组件。第 10 行获得我们定义的 computed 值并对其格式化。
number_format($this->liveUsers, 0)
因此整个组件长这样
<?php
use function Livewire\Volt\{state, computed};
use Illuminate\Support\Facades\Redis;
$liveUsers = computed(function () {
$count = 0;
$cursor = null;
$pattern = 'live_users:*';
do {
list($cursor, $keys) = Redis::scan($cursor, $pattern, 1000);
$count += count($keys ?? []);
} while ($cursor != 0);
return $count;
});
?>
<div>
<div wire:poll.5s>
<h3 class="text-base font-semibold leading-6 text-gray-900">Live stats</h3>
<dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div class="relative overflow-hidden rounded-lg bg-white px-4 pb-12 pt-5 shadow sm:px-6 sm:pt-6">
<dt>
<div class="absolute rounded-md bg-indigo-500 p-3">
<svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z"/>
</svg>
</div>
<p class="ml-16 truncate text-sm font-medium text-gray-500">Live Now</p>
</dt>
<dd class="ml-16 flex items-baseline">
<p class="text-2xl font-semibold text-gray-900">{{ number_format($this->liveUsers, 0) }}</p>
</dd>
</div>
</dl>
</div>
</div>
这就是我们要的 volt 组件
Demo
为了进行实际演示,我设置了一个控制台命令,用于模拟后台的用户活动。这将随机模拟活动用户,让您在网站上实时查看实时计数更新。
collect(Redis::keys('live_users:*'))->each(function ($key) {
Redis::del($key);
});
foreach(range(1, rand(10, 50)) as $userId) {
Redis::setex("live_users:$userId", 500, 1);
}