现象一个组件实现了raft分布式协议,在分布式部署环境中来进行选主,在某客户现场突然发生文件句柄泄露,在打印某些错误日志后,几个小时内没有日志打印,然后某个协程突然报无可用的文件句柄。分析经过代码和日志...

现象
一个组件实现了raft分布式协议,在分布式部署环境中来进行选主,在某客户现场突然发生文件句柄泄露,在打印某些错误日志后,几个小时内没有日志打印,然后某个协程突然报无可用的文件句柄。
分析
经过代码和日志分析,组件正常每分钟会打印所有部署节点的日志信息,没有打印日志说明定时器处理逻辑for...select里面某个函数逻辑卡住了,然后发生文件句柄泄露,经过梳理是在响应心跳的逻辑没有回,导致一直创建协程。心跳响应逻辑和定时器处理逻辑中有用到同一个锁,初步判断为这个锁发生死锁。
在本地环境复现了后,通过debug/pprof分析,确实有四处在等待该锁,两处等待写锁,两处等待读锁,但是代码看起来都很正常;pprof分析也没有提示死锁。然后通过搜索引擎搜索关键词“RWMutex 死锁”,找到一篇文件说RWMutex RLock重入可能导致死锁,如果网络异常,有分布式节点疑似下线时,代码中确实有一处会有该锁的RLock同一协程两次重入调用。
RLock重入死锁复现
1 func TestDeadLock(t *testing.T) { 2 var l sync.RWMutex 3 var wg sync.WaitGroup 4 wg.Add(2) 5 6 c := make(chan int) 7 go func() { 8 defer wg.Done() 9 10 l.RLock() 11 defer l.RUnlock() 12 t.Log("acquire RLock first") 13 14 c <- 1 15 runtime.Gosched() 16 17 t.Log("wait readLock") 18 l.RLock() 19 defer l.RUnlock() 20 t.Log("acquire RLock second") 21 }() 22 23 go func() { 24 defer wg.Done() 25 26 <-c 27 28 t.Log("wait writeLock") 29 l.Lock() 30 defer l.Unlock() 31 t.Log("acquire Lock") 32 }() 33 34 wg.Wait() 35 t.Log("test finish") 36 }
通过以上测试代码,很容易复现该死锁现象,而在java中可重入读写锁读锁重入不会导致死锁,所以刚开始看到RLock重入时也没有想到该问题。
源码分析
参考文档
golang RWMutex RLock重入导致死锁
本文标题为:golang RWMutex RLock重入导致死锁


基础教程推荐
- ruby-on-rails-使用Nginx的Rails的多阶段环境 2023-09-21
- R语言数可视化Split violin plot小提琴图绘制方法 2022-12-10
- asm基础——汇编指令之in/out指令 2023-07-06
- Go web部署报错panic: listen tcp xxxxxxx:8090: bind: cannot assign requested address 2023-09-05
- R语言基于Keras的MLP神经网络及环境搭建 2022-12-10
- swift版webview加载网页进度条效果 2023-07-05
- R包ggtreeExtra绘制进化树 2022-12-14
- swift 字符串String的使用方法 2023-07-05
- UEFI开发基础HII代码示例 2023-07-07
- R语言-如何将科学计数法表示的数字转化为文本 2022-11-23