系统环境

netty:netty3.2.4

linux:CentOS Linux release 7.1.1503

motan:开源版1.1.1

一、问题现象
1、访问服务连接不上,服务拒绝连接。流量突降触发报警。
2、有大量Too many files报错
3、认证服务返回403
4、NettyClient request Error 报错 分为
a、 java.lang.IllegalStateException: await*() in I/O thread causes a dead lock or sudden performance drop. Use addListener() instead or call await*() from a different thread.
b、 com.weibo.api.motan.exception.MotanServiceException: error_message: NettyChannel failed to connect to server, url: motan://10.22.6.156:8881/cn.sina.api.user.service.SinaUserMappingService, status: 503, error_code: 10001,r=0
Netty3 与基于Netty的rpc框架调用(触发死锁检查) 问题排查


1、根据1和2,得知现在我自己的业务系统(代理服务)的连接已经被打满。无法接收服务 access log已经没有流量进来了。
(lsof -p [pid] | wc -l 验证连接达到20w+,显示被TCP连接ESTABLISHED状态占满) tcp泄露问题
2、查看tcp的连接都是哪些资源,发现是用户关系方面的。
3、根据3、4a、4b 得知 是通过motan调用rpc服务导致tcp泄露。

先把结论放上来,带着结果看更清楚点

1、 不要在一个netty的I/O worker线程中开启中再次再次开启一个NettyChannel(nettty worker线程中再次使用netty千万要注意---netty4、5修改了具体原因请看下一篇解决方案) 如果worker中一定要再次使用,那就要自己新开线程来搞定了。
2、 motan是否可以在检查死锁触发后,关闭NettyChannel连接。(导致tcp连接泄露)


二、问题总结, netty的I/O work线程 通过motan调用接口 导致netty的死锁检查报错。
报错后其实创建channl是创建成功了 但是没有关闭channl连接并且无法复用
导致每次请求都会新建连接,tcp连接泄露打满docker中的连接数量 抛出too many files。

三、接下来就是寻找为什么会触发netty的死锁检查。

报错的栈信息
Netty3 与基于Netty的rpc框架调用(触发死锁检查) 问题排查
Netty3 与基于Netty的rpc框架调用(触发死锁检查) 问题排查


从错误栈看在什么时候触发的检查,寻找到以下代码 此处为motan源码,可以看到1处报错不能触发2关闭连接
Netty3 与基于Netty的rpc框架调用(触发死锁检查) 问题排查

Netty3 与基于Netty的rpc框架调用(触发死锁检查) 问题排查

触发死锁检查的代码,下面我们去看下实现。

Netty3 与基于Netty的rpc框架调用(触发死锁检查) 问题排查

经过分析isUseDeadLockChecker() 默认是true , 那么就是PARENT(一个ThreadLocal)搞得鬼。继续找在哪对PARENT操作了
Netty3 与基于Netty的rpc框架调用(触发死锁检查) 问题排查

就是在这里,会发现是开启I/O worker线程线程的时候塞入了PARENT, 并且死锁检查并不关心这里面是什么只要不为空就会抛出死锁风险异常

可以发现,是我本身的代理服务中启动了一个I/O worker线程,并且在这个线程中 调用motan open了一个NettyChannel时触发了checkDeadLock并且发现有死锁风险(PARENT不为空)

Netty3 与基于Netty的rpc框架调用(触发死锁检查) 问题排查


四、为什么PARENT不为空呢?
通过观察错误线程栈(图二)发现, motan open NettyChannel是时候是在我们本身netty工程的work线程中做的。
这样导致自身在open NettyChannel的时候在PARENT中放入值。 后来的motan open NettyChannel 时checkDeadLock发现有死锁危险,抛出异常。

五、问题总结
抛出的死锁检查异常导致我们没有做cancel(); 使得tcp连接连接占用满

六、结论
1、 不要在一个netty的I/O worker线程中开启中再次再次开启一个NettyChannel(nettty worker线程中再次使用netty千万要注意---netty4、5修改了具体原因请看下一篇解决方案) 如果worker中一定要再次使用,那就要自己新开线程来搞定了。
2、 motan是否可以在检查死锁触发后,关闭NettyChannel连接。(导致tcp连接泄露)
3、请期待下一篇, 定位后的解决方案。


相关文章: