websocket 实现了统计在线人数,那 websocket 该如何防爆,防跨站?
站点在线人数统计实现思路: https://www.douyacun.com/article/d189d3d86915f5ff4c3be6a517570a0a

- 如何检测跨站点 WebSocket 劫持漏洞
- 我使用 jwt 来下发 token
- 申请 token 的接口也是对外暴露的
- 同一设备 ws 连接数如何限制,避免 bug 导致连接数过多导致服务挂掉
站点在线人数统计实现思路: https://www.douyacun.com/article/d189d3d86915f5ff4c3be6a517570a0a

我之前的测试,库使用 gorilla/websocket 1 万个连接测试
占用 147.93MB RAM, 平均连接每个占用 15kb 测试代码见:[github gwebsocket]( https://github.com/douyacun/gwebsocket/blob/master/v3_ws_ulimit/wsserver.go)
“`shell
(pprof) top
Showing nodes accounting for 137.93MB, 93.24% of 147.93MB total
Dropped 6 nodes (cum <= 0.74MB)
Showing top 10 nodes out of 51
flat flat% sum% cum cum%
73.79MB 49.88% 49.88% 73.79MB 49.88% bufio.NewWriterSize
34.63MB 23.41% 73.29% 34.63MB 23.41% bufio.NewReaderSize
11MB 7.44% 80.73% 11MB 7.44% runtime.malg
4MB 2.70% 83.44% 5.50MB 3.72% net/textproto.(*Reader).ReadMIMEHeader
3MB 2.03% 85.46% 3.50MB 2.37% github.com/gorilla/websocket.newConn
3MB 2.03% 87.49% 10.50MB 7.10% net/http.readRequest
2.50MB 1.69% 89.18% 16.50MB 11.16% net/http.(*conn).readRequest
2.50MB 1.69% 90.87% 3.50MB 2.37% context.propagateCancel
2MB 1.35% 92.23% 2MB 1.35% syscall.anyToSockaddr
1.50MB 1.01% 93.24% 1.50MB 1.01% net.newFD
(pprof) web
failed to execute dot. Is Graphviz installed? Error: exec: “dot”: executable file not found in $PATH
(pprof) list flat
Total: 147.93MB
“`
goroutine 是 10003,每个 goroutine 占用 4kb 的内存
“`shell
(pprof) top
Showing nodes accounting for 10001, 100% of 10003 total
Dropped 24 nodes (cum <= 50)
Showing top 10 nodes out of 19
flat flat% sum% cum cum%
10001 100% 100% 10001 100% runtime.gopark
0 0% 100% 9998 100% bufio.(*Reader).Peek
0 0% 100% 9998 100% bufio.(*Reader).fill
0 0% 100% 9999 100% github.com/gorilla/websocket.(*Conn).NextReader
0 0% 100% 9999 100% github.com/gorilla/websocket.(*Conn).ReadMessage
0 0% 100% 9999 100% github.com/gorilla/websocket.(*Conn).advanceFrame
0 0% 100% 9998 100% github.com/gorilla/websocket.(*Conn).read
0 0% 100% 9999 100% internal/poll.(*FD).Read
0 0% 100% 10001 100% internal/poll.(*pollDesc).wait
0 0% 100% 10001 100% internal/poll.(*pollDesc).waitRead (inline)
(pprof) list flat
Total: 10003
(pprof)
“`
思路就是丢个唯一值到客户端 cookie,再把这个值丢到 redis 设置个超时,然后每次新请求获取这个值,如果这个值还在 redis 就不做操作,返回当前 redis 总计多少个 key 。
如果这个值不在 redis 里,就再丢一个唯一值到客户端,本地新增一个记录,返回 key 总数。
我认为 ws,做在线是非常好的方案,实时性和性能开销比都不错。
单设备连接数限制这个不太合理,通常应该按照身份限制比较好:既然有业务层认证,每个连接都有身份,如果不允许同一个身份多个连接、认证后就把之前的踢掉,如果允许,那就自己服务节点配置提高、节点数量增加之类的(如果怕统一身份的连接散到多个服务节点上,可以加个网关层,网关层按身份指定到实际的业务节点、由业务节点进行踢下线处理)。如果实在是想按照设备限制,那策略里使用身份的地方就改用 ip 或者你的算法能够生成的设备 id
统计人数通常不需要太精确,即使是多个服务节点,每个节点定时(比如 5s )更新自己节点在线数到 redis/sql 都可以、更新多节点在线数量总和就可以了,实时在线本来就是不停跳动的,精确的意义不大。如果实在要求精确,自己再写个服务进行统计、并且同步到所有节点,或者直接用 redis incr 之类的,每秒查询、更新,但是都没法保证百分百精确,实时的本来就是跳动的数据,即使是股票 K 线的蜡烛图也都是按时间段的起值、止值、最高值、最低值进行统计的
https://github.com/lesismal/arpc/tree/master/examples/webchat
另外 ARPC 支持发布订阅,如果想自己实现个管理服务器进行多个服务节点的在线数统计,管理服务器接收上报人数、然后把多节点的业务服在线总和发布就行了
想简单处理的话轮询写、读 redis 就好了
如果是觉得某个设备只要超过两三个连接就算过多,那就看我上一说过,限制机制自己订制下就好