现象一个组件实现了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 迭代器知识汇总 2023-07-23
- R语言关于二项分布知识点总结 2022-11-30
- Go Web开发进阶实战(gin框架) 2023-09-06
- 汇编语言程序设计之根据输入改变屏幕颜色的代码 2023-07-06
- Golang http.Client设置超时 2023-09-05
- Ruby的字符串与数组求最大值的相关问题讨论 2023-07-22
- R语言-如何切换科学计数法和更换小数点位数 2022-11-23
- Ruby on Rails在Ping ++ 平台实现支付 2023-07-22
- R语言绘图数据可视化pie chart饼图 2022-12-10
- Swift超详细讲解指针 2023-07-08