jetty9 websocket发送消息死锁,引发内存溢出

来源:互联网 发布:程序员常用工具 知乎 编辑:程序博客网 时间:2024/06/07 09:58

这个可能是jetty websocke考虑不周的地方,应该在lock.await()加个超时限制lock.await(10000),以防止这种异常不能释放锁的情况。

今天看了下tomcat websocket的实现,果然是有超时控制。

---------- 2017.7.14 补充 -------------------

最近在做一个金融项目,做了一个网关,用来对接各种终端,长连接不断推送期货行情。(jetty9 websocket)


长连接的方案采用websocket,上线前,做单个终端的压力测试,传输数百万数据,跑了10多小时,一切正常。但是上线后,发现时间不规则的内存溢出,有时可能4天就死了,有时半天就死了,甚是郁闷。


dump出内存堆,使用mat分析,很容易看到byte[]非常大,占60%内存。我们知道,当分析内存溢出的难点之一,就是没有特征类。这个时候需要去自己分析业务代码进行定位。我的网关功能,是从kafka上获取数据,然后通过websocket分发到各个终端。为了提高性能,内部有一个blockqueue作为内存缓冲队列,而blockqueue里面装的就是byte[]。然后通过mat查看byte[]内容,果不其然,就是队列的消息。剩下的问题就是追查为何内存队列消息没有被消费掉,奇怪的是,为什么网关一开始启动的时候,是可以正常工作的?


最开始以为消费线程死掉,经过一轮排查和测试后,发现不是这个问题。那会不会是线程死锁呢,通过jconsole查看线程,确实发现某个时刻,所有发送线程都在等待某一个线程,导致整个系统卡住。尝试增加一些同步代码到业务发送类,但是没有效果。再次回到观察线程死锁,发现源头是websocket内部的死锁,通过翻阅jetty源码,可以看到sendbytes后,有一个条件锁await的调用,这个时候就是等待,直到条件被激活,然后释放,发送完成。


可能到这里,大概能推断死锁的原因了。因为线上环境终端行为是比较复杂的,从错误日志里面经常可以看到各种异常断开连接的错误信息。如果在某个特殊情况下,向某个终端发送消息,这时终端异常断开,但是websocket这边并没有捕获到这个异常,就没法释放掉发送同步锁,从而导致死锁。追踪了一下jetty源码,因为比较复杂,没有找到释放的调用源头。


换一个思路去解决这个问题,使用异步发送方法。通过源码分析,异步发送跟同步发送,差异在于传入的callback不同。同步显然就是一个同步锁,异步就是future。也就是我们采用异步方法推送消息,不管结果如何,这样就能保证不死锁。对于我们的业务场景,确实也不需要保证发送成功,所以此方案可行。经过一段时间线上运行,内存溢出问题不再出现。

原创粉丝点击