Laravel 5.1 Redis 缓存配合 MySQL 数据库实现「用户最后活跃时间」功能
前言
今日给 PHPHub 开发了记录用户「最后活跃时间」的小功能,现在趁着热乎将相关实现逻辑写出来,欢迎大家指出不足。
基本思路
- 通过 Middleware 捕获用户的所有请求;
- 记录用户的请求时间,将数据放入缓存 (Redis) 中;
- 通过 Cron 每十分钟将缓存 (Redis) 里的数据同步到数据库中;
- 读取数据:先从 Redis 中读取数据,如 Redis 中没有,则从数据库中获取数据并同步到 Redis 中。
下文中我将针对每一个步骤进行详细说明。
捕获用户的所有请求
只有捕获用户的所有请求,我们才能实时的记录用户的最后活跃时间。在这里我们通过 中间件 来实现此逻辑。以下是生成中间件的具体操作:
1). 运行以下命令,生成 RecordLastActivedTime
中间件。
php artisan make:middleware RecordLastActivedTime
2). 在 app/Http/Kernel.php
中添加以下配置,将 RecordLastActivedTime
设置为全局中间件。
protected $middleware = [
...
\App\Http\Middleware\RecordLastActivedTime::class,
];
3). 在 app\Http\Middleware\RecordLastActivedTime.php
文件中添加逻辑代码
...
use Closure;
use Auth;
class RecordLastActivedTime
{
public function handle($request, Closure $next)
{
if (Auth::check()) {
// 在这里记录用户的访问时间,下文将介绍此方法。
Auth::user()->recordLastActivedAt();
}
return $next($request);
}
}
这样登录用户在访问我们网站任何一个页面时,都会进入到 RecordLastActivedTime
中,接下来我们将处理「记录用户请求时间」的逻辑。
记录用户的请求时间,将数据放入 Redis 中
现在,我们需要将用户的「最后活跃时间」存放到 Redis 中,并且接下来的读写操作都在 Redis 里进行,以减少数据库的开销。下面是具体操作:
1). 在 app/Models/User.php
中添加以下方法:
public function recordLastActivedAt()
{
$now = Carbon::now()->toDateTimeString();
// 这个 Redis 用于数据库更新,数据库每同步一次则清空一次该 Redis 。
$update_key = 'actived_time_for_update';
$update_data = Cache::get($update_key);
$update_data[$this->id] = $now;
Cache::forever($update_key, $update_data);
// 这个 Redis 用于读取,每次要获取活跃时间时,先到该 Redis 中获取数据。
$show_key = 'actived_time_data';
$show_data = Cache::get($show_key);
$show_data[$this->id] = $now;
Cache::forever($show_key, $show_data);
}
actived_time_for_update
和 actived_time_data
的数据结构如下:
array:4 [
1 => "2016-07-31 16:05:44"
2 => "2016-07-31 16:04:48"
3 => "2016-07-30 22:06:48"
4 => "2016-07-29 08:04:11"
]
2). 在 RecordLastActivedTime
中间件中调用 recordLastActivedAt
class RecordLastActivedTime
{
public function handle($request, Closure $next)
{
if (Auth::check()) {
Auth::user()->recordLastActivedAt();
}
return $next($request);
}
}
至此,只要登录的用户每访问一次网站,都会将其「最后活跃时间」记录到 Redis 中。
定期将 Redis 里的数据同步到数据库中
完成了上两步操作后,现在已经能获取到用户的「最后活跃时间」了,不过为了保证数据的完整性,我们需要定期将 Redis 数据同步到数据库中,否则一旦 Redis 出问题或者执行了 Redis 清理操作,用户的「最后活跃时间」将会丢失。
我的方案是编写一个同步命令 SyncUserActivedTime
,然后在计划任务里设置每 10 分钟运行该命令。以下是具体操作:
1). 运行以下命令添加 console
php artisan make:console SyncUserActivedTime --command=phphub:sync-user-actived-time
2). 在 app\Console\Commands\SyncUserActivedTime
添加逻辑代码
...
use Illuminate\Console\Command;
use App\Models\User;
use Cache;
class SyncUserActivedTime extends Command
{
protected $signature = 'phphub:sync-user-actived-time';
protected $description = 'Sync user actived time';
public function __construct()
{
parent::__construct();
}
public function handle()
{
// 注意这里获取的 Redis key 为 actived_time_for_update
// 获取完以后立马删除,这样就只更新需要更新的用户数据
$data = Cache::pull('actived_time_for_update'));
if (!$data) {
$this->error('Error: No Data!');
return false;
}
foreach ($data as $user_id => $last_actived_at) {
User::query()->where('id', $user_id)
->update(['last_actived_at' => $last_actived_at]);
}
$this->info('Done!');
}
}
3). 在 app/Console/Kernel.php
添加以下配置,生成 计划任务。
protected $commands = [
...
// 注册命令
Commands\SyncUserActivedTime::class,
];
protected function schedule(Schedule $schedule)
{
...
// 设置为每 10 分钟运行一次该命令。
$schedule->command('phphub:sync-user-actived-time')->everyTenMinutes();
}
获取用户的「最后活跃时间」
在 UserPresenter 添加以下方法
public function lastActivedAt()
{
$show_key = 'actived_time_data';
$show_data = Cache::get($show_key);
// 如果 Redis 中没有,则从数据库里获取,并同步到 Redis 中
if (!isset($show_data[$this->id])) {
$show_data[$this->id] = $this->last_actived_at;
Cache::forever($show_key, $show_data);
}
return $show_data[$this->id];
}
然后在需要展示的页面调用 lastActivedAt
即可,例如:
User::find(1)->present()->lastActivedAt;
如果你没有使用 present,可以将此方法写到
app/Models/User.php
中。
以上。
推荐文章: