Laravel如何判断用户是否在线?(实时状态)

2次阅读

在线判断需结合last_activity字段与心跳机制:后端在中间件中按最小间隔更新时间戳,前端定时发送认证心跳,数据库统一utc存储并加索引,高并发时缓存结果,强实时场景需websocket探活,且须明确定义“在线”业务含义。

Laravel如何判断用户是否在线?(实时状态)

last_activity 字段 + 心跳更新判断是否在线

laravel 自带的 users 表里有 last_activity(或类似字段),但默认不存、也不自动更新。得自己加逻辑:用户每次有效请求(比如访问需登录的页面、调接口)时,更新这个时间戳。

常见错误是只在登录时写一次,之后就不管了——结果用户开着页面半小时没操作,系统还显示“在线”。

  • 推荐在中间件里统一更新,比如新建 UpdateLastActivity 中间件,注册到 web
  • 别用 touch() 直接更新整个模型,避免触发不必要的事件或监听器
  • 更新语句建议用 DB::table('users')->where('id', auth()->id())->update(['last_activity' => now()]),轻量且可控
  • 注意并发:如果用户高频刷新或多个标签页同时发请求,不用锁表,但要避免频繁写磁盘,可考虑加个 30 秒最小间隔(缓存上次更新时间做判断)

前端定时发心跳,避免假离线

光靠后端请求更新不够——用户可能停留在某个页面不动,但浏览器还在运行。这时候得靠前端主动“报活”。

典型场景:后台管理页、聊天面板、协作编辑工具。不发心跳,5 分钟后用户就被标为离线,其实人还在盯着屏幕。

  • setInterval(() => axios.post('/api/heartbeat'), 60000),每分钟打一次,比 session 过期时间短即可
  • 心跳接口必须走 auth:sanctum 或对应 guard,否则未登录用户也能刷 last_activity
  • 别在 beforeunload 里发异步请求——浏览器可能直接关掉,发不出去;真要清理,用 navigator.sendBeacon 发个同步标记
  • 移动端要注意:webview 或 PWA 可能被系统休眠,心跳会断,得配合后台长连接兜底(见下一条)

数据库查“最近 X 分钟活跃”要小心时区和索引

判断是否在线,本质就是查 last_activity > now() - X minutes。但 Laravel 的 now() 是 PHP 时区,mysql 默认用系统时区,两边不一致会导致误判。

比如 PHP 设了 Asia/Shanghai,MySQL 用 UTC,那查出来的“5 分钟内”实际是错的。

  • 统一用 UTC 存储 last_activity,PHP 写入前转成 UTC:now()->utc(),查询也用 UTC 时间比较
  • last_activity 字段必须加索引,否则用户一多,WHERE last_activity > ? 就变全表扫描
  • 别用 User::where('last_activity', '>', now()->subMinutes(5))->get() 直接查全部——改成 select id, name, last_activity 只取必要字段,减少内存和网络开销
  • 如果并发查在线人数高(比如万人级实时看板),考虑把结果缓存 10 秒,用 Cache::remember('online_count', 10, fn() => ...)

WebSocket 不是必须,但复杂场景绕不开

http 心跳撑不住强实时要求:比如 IM 消息送达回执、协作文档光标同步、在线人数秒级刷新。这时候 HTTP 延迟+重试机制会让状态滞后 2~5 秒。

容易被忽略的是:WebSocket 连接建立后,服务端并不知道客户端是否真的“活着”——网络闪断、手机锁屏、浏览器挂起都可能导致连接假连。

  • 用 Laravel WebSockets 或 Soketi 时,务必开启 ping_intervalpong_timeout,让服务端主动探活
  • 客户端收到 ping 后必须立刻回 pong,不能等 js 线程空闲——要用 websocket.onping = () => websocket.pong() 这类底层钩子
  • 别把 WebSocket 连接状态和数据库 last_activity 混为一谈:前者反映“此刻 TCP 是否通”,后者反映“最近一次业务动作”,两者要分开维护、按需合并
  • 上线前压测真实弱网环境(比如用 chrome DevTools 的 “Slow 3G” + 随机断连),光本地跑通没用

实际最难的不是写代码,是定义“在线”本身——是 TCP 连着就算?还是得有鼠标移动?不同业务答案不同。先想清楚这个,再选技术方案。

text=ZqhQzanResources