添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

websocket如何实现统计当前在线人数

原创: golang 01/25/2021 发布 pv: 0 uv: 0 ip: 0 twitter # golang

原文地址: https://www.douyacun.com/article/d189d3d86915f5ff4c3be6a517570a0a

背景:之前开发的聊天是的功能,用的人太少了,考虑再三就决定去掉聊天室的功能了,改做一些其他有意思的事情,比如目前已经开发好了的左边栏当前在线人数,阅读时长统计(正在开发中)

遇到的问题:

  • 页面虽然是react写的,但是nextjs服务端渲染的,不是单页应用就不能像单页应用那样有全局变量存储整站的websocket
  • 服务端使用nginx作为代理服务器(之前是kong,后来干掉了),nginx proxy模块默认有 read_timeout/send_timeout 默认值 60s
  • 服务端socket存储数据结构选型
  • 实现思路:

    因为是多页模式,就需要每个页面初始化一个wss连接,按cookie去重以后就是当前在线人数。

    页面wss重联机制,毕竟断网是很常见的

    * websocket: 初始化 const connect = ( ) => { conn = new WebSocket (ws_address); conn. onmessage = handlerMessage; // ws 关闭需要重新连接,每隔1s尝试1次 conn. onclose = function ( e ) { console . log ( 'Socket is closed. Reconnect will be attempted in 1 second.' , e. reason ); setTimeout ( function ( ) { connect (); }, 1000 ); // 连接失败 conn. onerror = function ( e ) { console . error ( 'Socket encountered error: ' , e. message , 'Closing socket' ); conn. close ()

    nginx:proxy_read_timeout/proxy_send_timeout默认60s超时,心跳检测,只需要服务端在60s内定时发送ping(0x9)消息就可以了,浏览器会自动回复pong消息

    for {
      select {
        // 定时向客户端推送,当前在线人数
        case <-countTicker.C:
        c.send <- c.hub.Count().Bytes()
        // 定时心跳检测
        case <-pingTicker.C:
        _ = c.conn.SetWriteDeadline(time.Now().Add(writeWait))
        if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
          return
    

    服务端存储socket结构体,没有向指定socket写数据的需求,不需要额外维护socket的数据结构,维护一个cookie在线页面数即可

    type Hub struct {
    	// Registered uid
    	uuid map[string]int
    	// Inbound messages from the clients.
    	broadcast chan Responser
    	// Register requests from the clients.
    	register chan *Client
    	// Unregister requests from clients.
    	unregister chan *Client
    func (h *Hub) Run() {
      for {
        select {
          case client := <-h.register:
          // 注册ws
          h.uuid[client.uuid]++
          client.send <- hub.Count().Bytes()
          case client := <-h.unregister:
          // 删除ws
          h.uuid[client.uuid]--
          if h.uuid[client.uuid] == 0 {
            delete(h.uuid, client.uuid)
    

    安全方面需要考虑的内容是:

  • 如何检测跨站点 WebSocket 劫持漏洞?
  • 同一设备 ws 连接数如何限制,避免bug导致连接数过多导致服务挂掉?
  • 解决方法:

  • check origin, 确保是自己网站的来源
  •