一个组件实现了raft分布式协议,在分布式部署环境中来进行选主,在某客户现场突然发生文件句柄泄露,在打印某些错误日志后,几个小时内没有日志打印,然后某个协程突然报无可用的文件句柄。
分析经过代码和日志分析,组件正常每分钟会打印所有部署节点的日志信息,没有打印日志说明定时器处理逻辑for...select里面某个函数逻辑卡住了,然后发生文件句柄泄露,经过梳理是在响应心跳的逻辑没有回,导致一直创建协程。心跳响应逻辑和定时器处理逻辑中有用到同一个锁,初步判断为这个锁发生死锁。
在本地环境复现了后,通过debug/pprof分析,确实有四处在等待该锁,两处等待写锁,两处等待读锁,但是代码看起来都很正常;pprof分析也没有提示死锁。然后通过搜索引擎搜索关键词“RWMutex 死锁”,找到一篇文件说RWMutex RLock重入可能导致死锁,如果网络异常,有分布式节点疑似下线时,代码中确实有一处会有该锁的RLock同一协程两次重入调用。
RLock重入死锁复现通过以上测试代码,很容易复现该死锁现象,而在java中可重入读写锁读锁重入不会导致死锁,所以刚开始看到RLock重入时也没有想到该问题。
源码分析参考文档