站点在线人数统计实现思路: https://www.douyacun.com/article/d189d3d86915f5ff4c3be6a517570a0a
1
airyland 2021-01-25 10:57:15 +08:00
侧边栏弹出来遮住右侧内容是 feature?
|
2
ferock 2021-01-25 10:58:03 +08:00
用 tcp 长链统计在线人数???也是醉了
|
3
jobsofchina 2021-01-25 10:59:56 +08:00 via Android
@ferock 这种方式有什么问题吗?展开说说
|
4
notgod 2021-01-25 11:17:37 +08:00
@jobsofchina 量,N 万 百万 在线, 你试下
|
5
douyacun OP @ferock wss 统计在线人数,代价还是很低的,一般只是端口不够用
我之前的测试,库使用 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) ``` |
7
krixaar 2021-01-25 11:21:56 +08:00
@jobsofchina 第一,可能会有跨站点 WebSocket 劫持漏洞;第二,同一设备出 bug 导致连接数过多会让服务挂掉 🤣
|
8
zxlzy 2021-01-25 11:25:39 +08:00
在线人数这种需求建议还是轮询吧。
|
9
douyacun OP @krixaar 这几个问题也是这个帖子发布的目的,也是想看看大家是怎么玩的,有什么好的思路,之前有个安卓的同学就这么搞过我~,吐血的经历
|
11
rust 2021-01-25 12:53:43 +08:00
直接把用户登入的状态放在 Redis 里边啊,设置个超时时间,然后没有统计有多少数据存在不就行了
|
12
rust 2021-01-25 12:56:04 +08:00
@rust 我的错,没看清标题.
抖音 APP 在使用 WS 传输 Protobuf 编码之后的数据,同时也使用了 token,这也算是一个比较好的解决方案吧 |
14
SaltyLeo 2021-01-25 14:21:32 +08:00
额,不同的语言写法不同。
思路就是丢个唯一值到客户端 cookie,再把这个值丢到 redis 设置个超时,然后每次新请求获取这个值,如果这个值还在 redis 就不做操作,返回当前 redis 总计多少个 key 。 如果这个值不在 redis 里,就再丢一个唯一值到客户端,本地新增一个记录,返回 key 总数。 |
15
abersheeran 2021-01-25 14:27:30 +08:00
初次访问你网站的时候,下发一个 cookie. 后续 websocket 携带这个 cookie 去访问. 同一个 cookie 的直接 close 掉. 不带 cookie 的也 close 掉.
|
16
learningman 2021-01-25 17:19:17 +08:00
所有用户的这个值都应该是一样的啊,为啥要 ws
直接服务端统计然后直接暴露一个固定的接口轮询不就好了 |
17
hantsy 2021-01-25 19:54:40 +08:00
Client 与 Server 之间可以用 RabbitMQ 来缓和压力,这个在 Spring 中很好的支持,通过 STOMP 协议,客户端也用 sockjs 支持。
|
18
BBCCBB 2021-01-25 20:28:08 +08:00
设置一个能承受的连接上限, 比如好几十万. 网站最大就显示这么多个数..
|
19
BBCCBB 2021-01-25 20:28:26 +08:00
超过就直接连接失败
|
20
ihipop 2021-01-25 21:24:09 +08:00 via Android
@douyacun 一个端口服务能服务多少用户和服务器本地有多少个端口(也就是你认为的那个常规是 65535 的数值)没关系。
|
21
ihipop 2021-01-25 21:28:13 +08:00 via Android
|
22
caola 2021-01-25 21:40:21 +08:00
@douyacun #5 对外的 websocket 服务不是以端口为单位,而是以 URL 路径为单位的,websocket 可以发布于某个域名的某个路径或多个不同的路径下,与其他路径的 URL 页面或服务互不影响
|
23
cominghome 2021-01-26 08:14:17 +08:00
- -我一直以为这个数字是瞎整的(也不算瞎整,就是不需要那么严谨)
|
24
douyacun OP |
25
lesismal 2021-01-27 11:16:01 +08:00
跨站点劫持楼主已经写了,check origin + 业务层认证
单设备连接数限制这个不太合理,通常应该按照身份限制比较好:既然有业务层认证,每个连接都有身份,如果不允许同一个身份多个连接、认证后就把之前的踢掉,如果允许,那就自己服务节点配置提高、节点数量增加之类的(如果怕统一身份的连接散到多个服务节点上,可以加个网关层,网关层按身份指定到实际的业务节点、由业务节点进行踢下线处理)。如果实在是想按照设备限制,那策略里使用身份的地方就改用 ip 或者你的算法能够生成的设备 id 统计人数通常不需要太精确,即使是多个服务节点,每个节点定时(比如 5s )更新自己节点在线数到 redis/sql 都可以、更新多节点在线数量总和就可以了,实时在线本来就是不停跳动的,精确的意义不大。如果实在要求精确,自己再写个服务进行统计、并且同步到所有节点,或者直接用 redis incr 之类的,每秒查询、更新,但是都没法保证百分百精确,实时的本来就是跳动的数据,即使是股票 K 线的蜡烛图也都是按时间段的起值、止值、最高值、最低值进行统计的 |
26
lesismal 2021-01-27 11:21:06 +08:00
BTW,我这有个 ARPC 的 golang 框架提供了 websocket 聊天的简单示例
https://github.com/lesismal/arpc/tree/master/examples/webchat 另外 ARPC 支持发布订阅,如果想自己实现个管理服务器进行多个服务节点的在线数统计,管理服务器接收上报人数、然后把多节点的业务服在线总和发布就行了 想简单处理的话轮询写、读 redis 就好了 |
27
lesismal 2021-01-27 11:23:46 +08:00
节点数不多、redis 的话,每个节点每秒 hset 、hmget 下就行了,没啥压力,而且实时性也足够
|
28
lesismal 2021-01-27 11:27:05 +08:00
另外,怕连接数过多的话,单节点配置好最大在线数,新连接进来的时候判断下、超过了就拒绝掉,这个可以在网关或者业务节点的 upgrader checkorigion 里做,更好点的方式是自己 wrap 下 net.Listener,serve(listener),Accept 的地方直接做
|
29
lesismal 2021-01-27 11:33:38 +08:00
端口数量通常不是问题,文件描述符上限设置个 10w 、100w,其他的几个内核参数设置合理就行,只要你硬件配置足够。不过还真有的站点设置的不合理,golang 中国报错文件打开数量过多我就遇到过好多次。socket 是 4 元组,单 IP 自己过来的最大端口 65535,不代表服务器对所有 IP 加起来只能 65535,而且单 IP 除非故意写 bug 或者攻击、否则也不至于有这么多,而且这些 CDN 、防火墙那里就能挡,还轮不到业务层来处理这个(并且业务曾代码也没有这个能力处理)
如果是觉得某个设备只要超过两三个连接就算过多,那就看我上一楼说过,限制机制自己订制下就好 |