You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何统计Laravel项目当前运行的会话数量?

嘿,这个需求我之前帮不少开发者解决过!Laravel里要统计所有活跃会话——不管是登录用户还是逛公开页面的访客,核心得看你用的会话驱动是什么,不同驱动的实现方式差别不小,我给你一步步拆解:

一、先确认你的会话驱动

先去 .env 文件里看 SESSION_DRIVER 的值,或者直接查 config/session.php 里的配置。Laravel 默认是 file 驱动,但生产环境很多人会用 database 或 redis——这三种是最常见的,我分别给你讲怎么统计:

二、不同驱动的实现方法

1. Database驱动(最省心的方案)

如果用的是 database 驱动,首先得确保你已经生成了会话表:没生成的话跑 php artisan session:table 然后执行迁移。默认会话表叫 sessions,里面的字段刚好能满足统计需求:user_id(登录用户ID,未登录是null)、last_activity(最后活跃时间戳)、ip_address 这些。

统计逻辑

我们通常把“当前活跃”定义为最近N分钟内有过交互(N就是你在 config/session.php 里配置的 lifetime,默认120分钟)。代码示例:

use Illuminate\Support\Facades\DB;

// 计算会话过期时间点
$expiration = now()->subMinutes(config('session.lifetime'));

// 总活跃会话数
$totalSessions = DB::table('sessions')
    ->where('last_activity', '>=', $expiration->timestamp)
    ->count();

// 登录用户数(同一个用户可能开多个会话,要去重)
$loggedInUsers = DB::table('sessions')
    ->where('last_activity', '>=', $expiration->timestamp)
    ->whereNotNull('user_id')
    ->distinct('user_id')
    ->count();

// 访客数 = 总会话数 - 登录用户数
$guests = $totalSessions - $loggedInUsers;

查看详细会话信息

如果要展示每个会话的具体数据(比如用户名称、IP、设备),可以关联用户表查询:

$activeSessions = DB::table('sessions')
    ->where('last_activity', '>=', $expiration->timestamp)
    ->leftJoin('users', 'sessions.user_id', '=', 'users.id')
    ->select(
        'sessions.id as session_id',
        'users.name as user_name',
        'users.email as user_email',
        'sessions.ip_address',
        'sessions.user_agent',
        DB::raw('FROM_UNIXTIME(sessions.last_activity) as last_active')
    )
    ->get();

2. File驱动(需要遍历本地文件)

file驱动是Laravel默认的,但统计起来最麻烦——会话都存在 storage/framework/sessions 目录下,每个文件是序列化的会话数据,文件名就是会话ID。

统计逻辑

需要遍历所有会话文件,检查文件修改时间(或者解析内容里的last_activity),过滤掉过期的:

use Illuminate\Support\Facades\Storage;
use Illuminate\Session\Store;
use Illuminate\Support\Facades\Auth;

$sessionDir = storage_path('framework/sessions');
$expiration = now()->subMinutes(config('session.lifetime'))->timestamp;

$totalSessions = 0;
$loggedInUsers = 0;
$loggedInUserIds = [];

// 遍历所有会话文件
foreach (Storage::files($sessionDir) as $file) {
    $fileModifiedTime = Storage::lastModified($file);
    
    // 只统计未过期的会话
    if ($fileModifiedTime >= $expiration) {
        $totalSessions++;
        
        // 解析会话内容
        $sessionContent = Storage::get($file);
        
        // 如果会话加密了,必须用SessionStore解密
        if (config('session.encrypt')) {
            $sessionStore = app(Store::class);
            $sessionData = $sessionStore->getPayloadFromRaw($sessionContent);
        } else {
            $sessionData = unserialize($sessionContent);
        }
        
        // 获取登录用户ID(Laravel默认的认证会话键可以用Auth::getRecallerName()获取)
        $userId = $sessionData[Auth::getRecallerName()] ?? null;
        if ($userId && !in_array($userId, $loggedInUserIds)) {
            $loggedInUserIds[] = $userId;
            $loggedInUsers++;
        }
    }
}

$guests = $totalSessions - $loggedInUsers;

3. Redis驱动(利用Redis键匹配)

如果用Redis存储会话,键名一般是 laravel:session:xxxxxx(前缀看 config/session.php 里的 prefix 配置),我们可以通过匹配键名来统计:

统计逻辑

use Illuminate\Support\Facades\Redis;
use Illuminate\Session\Store;
use Illuminate\Support\Facades\Auth;

$sessionPrefix = config('session.prefix', 'laravel:session');
$expirationMinutes = config('session.lifetime');

// 获取所有会话键
$sessionKeys = Redis::keys("{$sessionPrefix}:*");

$totalSessions = 0;
$loggedInUsers = 0;
$loggedInUserIds = [];

foreach ($sessionKeys as $key) {
    // 检查键的剩余过期时间,大于0说明未过期
    $ttl = Redis::ttl($key);
    if ($ttl > 0) {
        $totalSessions++;
        
        // 解析会话数据
        $sessionData = Redis::get($key);
        if (config('session.encrypt')) {
            $sessionStore = app(Store::class);
            $sessionData = $sessionStore->getPayloadFromRaw($sessionData);
        } else {
            $sessionData = unserialize($sessionData);
        }
        
        // 统计登录用户
        $userId = $sessionData[Auth::getRecallerName()] ?? null;
        if ($userId && !in_array($userId, $loggedInUserIds)) {
            $loggedInUserIds[] = $userId;
            $loggedInUsers++;
        }
    }
}

$guests = $totalSessions - $loggedInUsers;
三、通用优化建议
  • 缓存统计结果:遍历文件/Redis、查询数据库都挺耗时的,建议用 Cache::remember 把统计结果缓存5-10分钟,避免每次请求都重复计算。
  • 处理加密会话:如果开启了 config('session.encrypt') = true,一定要用 Store 类的 getPayloadFromRaw 方法解密,直接反序列化会报错。
  • 权限控制:统计会话的页面要加权限验证,比如只允许管理员访问,避免敏感数据泄露。
四、快速做个展示页面

可以写个简单的控制器方法,把统计结果展示出来:

// routes/web.php 加路由(记得加auth中间件)
Route::get('/admin/sessions', [SessionController::class, 'index'])->middleware('auth');

// 控制器方法
public function index()
{
    $expiration = now()->subMinutes(config('session.lifetime'));
    $totalSessions = 0;
    $loggedInUsers = 0;
    $activeSessions = collect();

    switch(config('session.driver')) {
        case 'database':
            $totalSessions = DB::table('sessions')->where('last_activity', '>=', $expiration->timestamp)->count();
            $loggedInUsers = DB::table('sessions')->where('last_activity', '>=', $expiration->timestamp)->whereNotNull('user_id')->distinct('user_id')->count();
            $activeSessions = DB::table('sessions')
                ->where('last_activity', '>=', $expiration->timestamp)
                ->leftJoin('users', 'sessions.user_id', '=', 'users.id')
                ->select('sessions.id', 'users.name', 'users.email', 'sessions.ip_address', 'sessions.user_agent', DB::raw('FROM_UNIXTIME(sessions.last_activity) as last_active'))
                ->get();
            break;
        // 其他驱动的逻辑,比如file/redis
        // case 'file': ...
        // case 'redis': ...
    }

    $guests = $totalSessions - $loggedInUsers;
    return view('admin.sessions', compact('totalSessions', 'loggedInUsers', 'guests', 'activeSessions'));
}

然后在视图 resources/views/admin/sessions.blade.php 里展示:

<div class="grid grid-cols-3 gap-4 mb-6">
    <div class="p-4 bg-blue-50 rounded-lg">
        <h3 class="text-sm font-medium text-gray-600">总活跃会话</h3>
        <p class="text-2xl font-bold text-blue-600">{{ $totalSessions }}</p>
    </div>
    <div class="p-4 bg-green-50 rounded-lg">
        <h3 class="text-sm font-medium text-gray-600">登录用户</h3>
        <p class="text-2xl font-bold text-green-600">{{ $loggedInUsers }}</p>
    </div>
    <div class="p-4 bg-yellow-50 rounded-lg">
        <h3 class="text-sm font-medium text-gray-600">访客</h3>
        <p class="text-2xl font-bold text-yellow-600">{{ $guests }}</p>
    </div>
</div>

<table class="w-full border-collapse">
    <thead>
        <tr class="bg-gray-100">
            <th class="px-4 py-2 text-left">会话ID</th>
            <th class="px-4 py-2 text-left">用户</th>
            <th class="px-4 py-2 text-left">IP地址</th>
            <th class="px-4 py-2 text-left">设备</th>
            <th class="px-4 py-2 text-left">最后活跃</th>
        </tr>
    </thead>
    <tbody>
        @foreach($activeSessions as $session)
        <tr class="border-t">
            <td class="px-4 py-2">{{ substr($session->id, 0, 10) }}...</td>
            <td class="px-4 py-2">{{ $session->name ?? '访客' }}</td>
            <td class="px-4 py-2">{{ $session->ip_address }}</td>
            <td class="px-4 py-2 text-sm">{{ substr($session->user_agent, 0, 60) }}...</td>
            <td class="px-4 py-2 text-sm">{{ $session->last_active }}</td>
        </tr>
        @endforeach
    </tbody>
</table>

内容的提问来源于stack exchange,提问作者Optimus Prime

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:11:12