socket消息推送延迟-fwrite引发的血案

最近有同事反映,自己的推送消息延迟严重,最长的大概一分钟左右,导致相关业务逻辑受影响,但是类似的业务在JS client 却没有问题,只是在PHP client上延迟严重。该业务对消息及时性要求非常高,于是领导要求尽快查清原因,并解决问题。

问题重现

同事给我现场演示,发现问题确实存在。以前并没有遇到此类问题,直觉猜想可能跟前几天为解决带宽占用问题,使用的消息队列有关系。检查消息队列的日志,发现,消息队列并无延迟现象。可是推送服务器由于还有别的业务模块在用,并没有受到此延迟影响,这就比较挠头。先上整个调用逻辑过程图:

业务逻辑

            图1 整体业务示意图

问题分析(排除法)

  1. 最近上线的是mns模块,是mns返回消息延迟了么?
    同事给我展示消息写入mns之前的日志和接收mns时的日志,对比发现,二者相差无几,几乎同1秒之内就返回。那一定不是mns的锅了,之前还有担心过,远程的消息队列是否可靠。但是日志是不会骗人的,目前来看,并不是它的问题。(我们一度想要改方案,自己搭建redis消息队列)

  2. 既然不是mns返回延迟,难道是xmpp server 推送延迟了?
    检查server 负载、端口和带宽占用情况,发现服务器负载很低。由于之前没有编译日志模块,无法百分之百排除是服务器的问题。(日志模块,非官方自带,需要单独编译,某些原因,编译总失败,郁闷。。。)但是单独模拟调用,发现消息是秒到的。这个也可以证明server的清白。(事实确实是清白的)

  3. 既然99%不是server推送延迟,难道是接收端的逻辑报错,业务进行不下去了?
    排查websocket端的时间和页面js控制台信息,发现业务并没有受影响。收到消息之后很快就处理完毕了。额。。。

  4. 那问题只能是出在long run 脚本调用server 这里了?
    检查逻辑发现,这个脚本并没有异常情况。世界上最难过的事儿,莫过于看起来一切都没问题,可是他就是有问题。到底是什么原因引起的呢?客户已经嚷嚷着要退款了,临时方案是改回去代码。先让客户正常使用。

  5. 水落实出
    由于我们半天一直没有拿下,到下班之前,我一直在致力于证明server 那1%的证据,CTO坐不住了,亲自上手来查。他单步调试,发现了导致慢的症结。
    长跑程序本质是如下的代码:

while(1) {
   send(buf) //send的实现是调用fwrite(buf)
}

fwrite/fread都是操作系统经过buffer虚拟出来的I/O接口,他不同于裸的无buffer的文件I/O操作:read/write。当一个进程连续调用fwrite的时候,只有操作系统的buffer写满了,它才会真正把数据“写”进去。所以要即时发送,比如每次send后,应该调用一下fflush() 数据才会真的发出去,否则它只是被拷贝到了buffer里,这个buffer大小大概8K字节,不同的操作系统不同。那么为什么非长跑程序版本没问题呢:因为进程退出的时候,该进程对应的I/O句柄都需要关闭,关闭之前会自动fflush()。刚好符合:
1. 问题出在给IM服务器发送到从IM服务器接收之间
2. IM服务器本身没问题(虽然我一直相信他是清白的)

我们用的开源库,并没有说过这个问题,long run脚本也是作者提供的。所以我紧急上线加了强制刷新,果然,消息不再延迟。

反思

正如cto说的,一个人的知识面深度和广度决定了他解决问题的能力和手段。比如系统内核参数的问题,你在应用程序上查,确实很难发现问题源头。自己还得多多努力,向前辈致敬!

最后附上关于buffer的相关连接,有需要的同学们可以看下:
fwrite,fflush,你不知道的事!

猜你喜欢

转载自blog.csdn.net/qq_30164225/article/details/80714310