项目背景
Server端使用netty 4,Client端使用websocket与server交互。
client与server有两种交互方式(如下图):
从图中可以看出,对于 server -> client B(标红的那段),简单来说分两种:
1、client B 主动请求后的response
2、server的主动push
client与server的两种交互方式乱序问题
无论是response还是push,在服务端都是使用netty的ctx.channel().writeAndFlush()。但是在并发情况下,发现某些消息到达客户端是乱序的,如下图。
乱序问题经过一段追查后终于找到原因,是netty线程模型搞的鬼。netty线程模型的介绍可以参见Netty版本升级血泪史之线程篇的第6点。
简单来说就是,netty 4中,使用ctx.channel().writeAndFlush()时,会去检查 当前线程 是否为 netty为channel分配的eventLoop所对应的线程,如果是的话就直接调用,不是的话就封装成一个task提交到eventLoop的任务队列中。
对应到本case中,因为对于request - response方式,我是直接在netty的work线程(netty为channel分配的eventLoop所对应的线程)中直接处理request,并回response。所以对于给Client B的response,调用Client B的ctx.channel().writeAndFlush()是直接写出,只要在业务层面上保证调用ctx.channel().writeAndFlush()是有序的,那么写出到Client B就是有序的。
但是对于push方式,是在其他线程中调用Client B的ctx.channel().writeAndFlush(),这时候会把消息封装成task提交到Client B的eventLoop任务队列中,与response消息混在一起。在并发情况下,即便在业务层上做了有序性的保证,但无法去保证netty是先执行task的消息,还是先执行那种直接调用ctx.channel().writeAndFlush()的消息。
解决方案
业务层面上,对消息增加时序性保证的id(我这里采用的是递增id),端上接收到消息后,按照id去做消息重排序,这样就能保证消息按序到达。
网友评论