本文目录导读:

- 方案一:基于Session的简单实现
- 方案二:使用Redis/Predis存储在线状态(推荐)
- 方案三:使用Laravel Echo + Socket.IO(实时)
- 方案四:API接口实现(适用于前后端分离)
- 最佳实践建议
- 使用示例
我来为您介绍在Laravel项目中实现在线用户状态的几种方案:
基于Session的简单实现
数据库迁移
// database/migrations/create_sessions_table.php
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity')->index();
$table->timestamps();
});
创建在线状态中间件
// app/Http/Middleware/UpdateUserActivity.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
class UpdateUserActivity
{
public function handle($request, Closure $next)
{
if (Auth::check()) {
$expiresAt = now()->addMinutes(5); // 5分钟后过期
Cache::put('user-is-online-' . Auth::user()->id, true, $expiresAt);
Auth::user()->update([
'last_seen_at' => now()
]);
}
return $next($request);
}
}
配置中间件
// app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
// ...
\App\Http\Middleware\UpdateUserActivity::class,
],
];
用户模型更新
// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class User extends Model
{
// 判断用户是否在线
public function isOnline()
{
return Cache::has('user-is-online-' . $this->id);
}
// 获取在线用户
public static function onlineUsers()
{
return self::where('last_seen_at', '>=', now()->subMinutes(5))
->get();
}
}
使用Redis/Predis存储在线状态(推荐)
安装Redis
composer require predis/predis
配置Redis
// config/database.php
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
],
创建在线用户服务
// app/Services/OnlineUserService.php
namespace App\Services;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redis;
class OnlineUserService
{
private const ONLINE_KEY = 'online_users';
private const TIMEOUT = 300; // 5分钟
public function markOnline($userId)
{
Redis::zadd(self::ONLINE_KEY, time(), $userId);
Redis::expire(self::ONLINE_KEY, self::TIMEOUT);
}
public function markOffline($userId)
{
Redis::zrem(self::ONLINE_KEY, $userId);
}
public function getOnlineUsers()
{
$onlineUsers = Redis::zrange(self::ONLINE_KEY, 0, -1);
return array_map('intval', $onlineUsers);
}
public function isUserOnline($userId)
{
$score = Redis::zscore(self::ONLINE_KEY, $userId);
return $score !== false && (time() - $score) < self::TIMEOUT;
}
public function cleanExpired()
{
$expireTime = time() - self::TIMEOUT;
Redis::zremrangebyscore(self::ONLINE_KEY, 0, $expireTime);
}
}
创建事件和监听器
// app/Events/UserOnline.php
namespace App\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserOnline
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $userId;
public function __construct(int $userId)
{
$this->userId = $userId;
}
}
监听器
// app/Listeners/HandleUserOnline.php
namespace App\Listeners;
use App\Events\UserOnline;
use App\Services\OnlineUserService;
class HandleUserOnline
{
private $onlineService;
public function __construct(OnlineUserService $onlineService)
{
$this->onlineService = $onlineService;
}
public function handle(UserOnline $event)
{
$this->onlineService->markOnline($event->userId);
}
}
在线状态控制器
// app/Http/Controllers/OnlineStatusController.php
namespace App\Http\Controllers;
use App\Services\OnlineUserService;
use Illuminate\Http\Request;
class OnlineStatusController extends Controller
{
private $onlineService;
public function __construct(OnlineUserService $onlineService)
{
$this->onlineService = $onlineService;
}
// 更新在线状态(前端定时请求)
public function updateStatus(Request $request)
{
if ($request->user()) {
$this->onlineService->markOnline($request->user()->id);
return response()->json(['success' => true]);
}
return response()->json(['error' => 'Unauthorized'], 401);
}
// 获取在线用户列表
public function onlineUsers()
{
$onlineUsers = $this->onlineService->getOnlineUsers();
$users = \App\Models\User::whereIn('id', $onlineUsers)
->select('id', 'name', 'avatar')
->get();
return response()->json(['users' => $users]);
}
}
使用Laravel Echo + Socket.IO(实时)
安装依赖
composer require pusher/pusher-php-server npm install laravel-echo pusher-js
配置广播
// config/broadcasting.php
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_CLUSTER'),
'encrypted' => true,
],
],
],
创建用户在线状态事件
// app/Events/UserPresenceChanged.php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
class UserPresenceChanged implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets;
public $userId;
public $isOnline;
public function __construct($userId, $isOnline)
{
$this->userId = $userId;
$this->isOnline = $isOnline;
}
public function broadcastOn()
{
return new Channel('users');
}
public function broadcastAs()
{
return 'user.presence.changed';
}
}
前端实时监听
// resources/js/bootstrap.js
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
forceTLS: true
});
// 监听用户状态变更
Echo.channel('users')
.listen('.user.presence.changed', (e) => {
updateUserStatus(e.userId, e.isOnline);
});
API接口实现(适用于前后端分离)
在线状态表迁移
// database/migrations/create_user_presence_table.php
Schema::create('user_presence', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->unique();
$table->timestamp('last_seen_at');
$table->string('ip_address')->nullable();
$table->string('user_agent')->nullable();
$table->boolean('is_online')->default(false);
$table->timestamps();
});
心跳接口
// routes/api.php
Route::middleware('auth:sanctum')->post('/presence/heartbeat', function (Request $request) {
$user = $request->user();
$presence = UserPresence::updateOrCreate(
['user_id' => $user->id],
[
'last_seen_at' => now(),
'is_online' => true,
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent()
]
);
return response()->json(['success' => true]);
});
// 获取在线用户
Route::get('/presence/online-users', function () {
$onlineUsers = UserPresence::where('is_online', true)
->where('last_seen_at', '>=', now()->subMinutes(5))
->with('user:id,name,avatar')
->get();
return response()->json($onlineUsers);
});
前端心跳实现
// 前端心跳更新
function updatePresence() {
axios.post('/api/presence/heartbeat')
.then(response => {
console.log('Presence updated');
})
.catch(error => {
console.error('Error updating presence:', error);
});
}
// 每4分钟更新一次
setInterval(updatePresence, 4 * 60 * 1000);
最佳实践建议
中间件自动更新
// app/Http/Middleware/AutoUpdatePresence.php
namespace App\Http\Middleware;
use Closure;
use App\Services\OnlineUserService;
class AutoUpdatePresence
{
protected $onlineService;
public function __construct(OnlineUserService $onlineService)
{
$this->onlineService = $onlineService;
}
public function handle($request, Closure $next)
{
$response = $next($request);
if (auth()->check()) {
$this->onlineService->markOnline(auth()->id());
}
return $response;
}
}
清理过期数据
// 使用定时任务清理过期数据
// app/Console/Commands/CleanExpiredPresence.php
class CleanExpiredPresence extends Command
{
protected $signature = 'presence:clean';
public function handle(OnlineUserService $service)
{
$service->cleanExpired();
$this->info('Cleaned expired presence data.');
}
}
缓存优化
// 使用缓存提高性能
$onlineStatus = Cache::remember("user-presence-{$userId}", 60, function () use ($userId) {
return $this->onlineService->isUserOnline($userId);
});
异步处理
// 将在线状态更新放到队列中
Illuminate\Support\Facades\Queue::push(function ($job) {
$job->setTimeout(5);
$service = app(OnlineUserService::class);
$service->markOnline($job->userId);
});
使用示例
// 在视图中显示用户在线状态
@if($user->isOnline())
<span class="online-status">在线</span>
@else
<span class="offline-status">离线</span>
@endif
// 获取在线用户数 $onlineCount = $onlineUserService->getOnlineUsersCount();
// 在管理员面板显示在线用户
foreach ($onlineUsers as $user) {
echo "{$user->name} - 最后活跃:{$user->last_seen_at->diffForHumans()}";
}
选择哪种方案取决于您的具体需求:
- 简单场景:使用Session方案
- 多服务器场景:使用Redis方案
- 实时性要求高:使用WebSocket方案
- 前后端分离:使用API方案
记得根据实际情况调整超时时间和心跳频率。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播