beego无内置全局在线人数统计,因缺乏application对象且包级变量非线程安全;多进程下计数隔离,且session销毁无法感知异常断连;推荐用redis set存活跃用户id并配心跳机制实时维护。

Beego 本身不提供开箱即用的全局在线人数统计机制,必须自己实现会话生命周期监听 + 线程安全计数器,否则数据必然不准。
为什么不能直接用 Application 或全局变量
Beego 没有 Application 对象(那是 ASP.NET 或 Java Servlet 的概念),Go 语言也没有跨请求共享的“应用级上下文”原语。你如果写 var OnlineCount int 在包级作用域:
- 在多 goroutine 并发场景下,
OnlineCount++和OnlineCount--不是原子操作,会丢失更新 - beego 启动多个进程(如用 supervisor 或 systemd 多实例)时,每个进程维护自己的计数,完全无法反映真实在线数
- 没有自动感知会话超时或异常断连的能力,只靠请求进/出计数,容易虚高
推荐方案:用 Redis + beego.Session 存储 + 定时清理
这是生产环境最可靠的做法,兼顾准确性、分布一致性与可扩展性:
- 每次用户登录或建立有效会话时,向 Redis 写入一个带过期时间的 key,例如
online:user:12345,TTL 设为 session 过期时间(如 30 分钟) - 使用
beego.BConfig.WebConfig.Session.SessionProvider = "redis"并配置好SessionProviderConfig,确保 session 数据落地 Redis - 统计时执行
KEYS online:user:*是反模式,应改用 Redis 的SCAN命令分批遍历,或更优地——改用SET类型存所有活跃 user_id,用SCARD online:users直接获取数量 - 需要额外起一个 goroutine,每 2–5 分钟扫描一次
online:users中的成员,剔除已过期或无对应 session 的条目(Redis 的 key 过期是被动触发,不能完全依赖)
如何在 Beego 控制器中触发增减逻辑
不要在每个 handler 里手写 incr/decr,而是统一收口到中间件或基类:
- 在自定义
BaseController的Prepare()方法中检查this.GetSession("user_id") != nil,若存在且尚未计入,则调用redis.SAdd("online:users", userID)并设 TTL - 不要依赖
Finish()清理——HTTP 请求结束 ≠ 用户下线;应由前端定时心跳(如每 60s POST /api/keepalive)来刷新 Redis key 的 TTL - 退出登录接口(如
Logout)中显式调用redis.SRem("online:users", userID) - 避免在
Prepare()中做阻塞 Redis 调用;建议用连接池(如github.com/go-redis/redis/v8)并设置超时(Timeout: 300 * time.Millisecond)
最容易被忽略的点:session 与在线状态不是一回事
用户关闭浏览器、网络中断、手机切后台,都不会触发 Beego 的 session 销毁逻辑。所以:
- 纯靠 session 创建/销毁事件统计,结果一定偏高,且滞后期长
- 必须引入客户端主动上报机制(心跳),服务端只信任最近 N 分钟内有过心跳的用户
- Redis key 的 TTL 必须严格等于心跳周期 × 容忍倍数(例如心跳 60s,容忍 3 次失败 → TTL = 180s),不能拍脑袋设成 30 分钟
- 如果你的应用已有 WebSocket 连接,优先从
onClose回调里做SRem,比 HTTP 心跳更实时、更准确











