之所以说”不完全解决”,是因为解决方式不能称之为”解决方案”,只是避免了这个问题的发生(尴尬)~

bug 现象描述

故障现象需要使用到多个客户端连接,才能看到效果。表现形式语言很难描述(跟前一个 修复 Socket.IO Multiple Sockets open after reconnect #430 那个一样很难描述,难道我的语文老师是体育老师兼职的??)。

业务很简单:每次有新的连接生成的 Socket 都会加入同一个 room 中去。然后:

Socket.id 为 A1 的客户端连接成功后,此时 room 中的 Socket 是

1
['A1']

Socket.id 为 A2 的客户端连接成功后,此时 room 中的 Socket 是

1
['A2'] // what f**k ?

刷新 A2,此时 room 中的 Socket 是

1
['A2', 'A1'] // work fine ~

刷新 A2 again,此时 room 中的 Socket 是

1
['A2'] // what f**k again ?!

刷新 A2 triple,此时 room 中的 Socket 是

1
['A2', 'A1'] // ......

如此不断重复刷新 A2 就会看到 room 中一会儿有 A1,一会没有。。。难以描述。。。

bug 成因分析

一开始是完全没有头绪,你要么都在 room 中,要么都不在,特么一下在一下不在的,真是无语。。。

没办法,只能去看 issues 了。翻来翻去忽然看到这么一句话 Rooms is not same in all process #2330,心中忽然一亮:莫不是多实例造成的?

回想起先前的 pm2 配置参数如下

1
2
3
4
{
"exec_mode": "cluster",
"instances": 0
}

启动 Node 实例时使用了 cluster mode,这时 pm2 会根据当前服务器的状态,尽可能的利用 CPU 多核的优势提高实例的工作能力。

这是 pm2 文档中的 原文

The -i or instances option can be:

  • 0 to spread the app across all CPUs
  • -1 to spread the app across all CPUs - 1
  • number to spread the app across number CPUs

当时这台服务器是双核的 CPU,因此按照这个配置策略启动的实例,在 pm2 里面其实是两个进程(Process)。问题一定出现在这里。

这就解释了为啥 A1 一会在一会不在,应该是 pm2 为了均衡多核 CPU 的工作负载,将客户端以轮询的方式命中两个进程导致的。

解决方案

找到问题,就好办了:调整 pm2 配置参数,强制使用一个进程运行实例

1
2
3
4
{
"exec_mode": "cluster",
"instances": 1
}

再次如前方法测试,room 里面乖乖的躺着全部连接上来的客户端了~

done~