|
26 | 26 |
|
27 | 27 | 问题本质 : |
28 | 28 |
|
29 | | - - 连接异常处理机制缺陷 |
30 | | - |
31 | | - - 并发协程间的资源竞争 |
32 | | - |
33 | | - - 连接关闭时的任务执行完整性问题 |
| 29 | + - 连接异常处理机制缺陷 |
| 30 | + |
| 31 | + - 并发协程间的资源竞争 |
34 | 32 |
|
| 33 | + - 连接关闭时的任务执行完整性问题 |
| 34 | + |
35 | 35 | 改进方案 : |
36 | 36 |
|
37 | | - - 状态管理 : |
38 | | - |
39 | | - - 新增原子变量标记连接状态 |
40 | | - |
41 | | - - 使用sync.WaitGroup跟踪任务执行 |
| 37 | + - 状态管理 : |
42 | 38 |
|
43 | | - - 执行流程优化 : |
| 39 | + - 新增原子变量标记连接状态 |
| 40 | + |
| 41 | + - 使用sync.WaitGroup跟踪任务执行 |
| 42 | + |
| 43 | + - 执行流程优化 : |
44 | 44 |
|
45 | | - ``` |
46 | | - // 连接异常处理流程 |
47 | | - reader检测异常 -> 设置关闭状态 -> 触发stop流程 -> |
48 | | - wg.Wait等待已接收任务 -> workpool继续执行遗留任务 -> |
49 | | - writer无脑发送(忽略错误) -> wg.Done -> 清理资源(关闭msg管道等) |
50 | | - ``` |
| 45 | + ``` |
| 46 | + // 连接异常处理流程 |
| 47 | + reader检测异常 -> 设置关闭状态 -> 触发stop流程 -> |
| 48 | + wg.Wait等待已接收任务 -> workpool继续执行遗留任务 -> |
| 49 | + writer无脑发送(忽略错误) -> wg.Done -> 清理资源(关闭msg管道等) |
| 50 | + ``` |
51 | 51 |
|
52 | | - - 容错设计 : |
53 | | - - 任务提交阶段不进行连接状态检查 |
| 52 | + - 容错设计 : |
| 53 | + - 任务提交阶段不进行连接状态检查 |
| 54 | + |
| 55 | + - 写协程统一处理发送异常 |
54 | 56 | |
55 | | - - 写协程统一处理发送异常 |
56 | | - |
57 | | - - 提供用户级熔断接口(通过handler提前return实现) |
| 57 | + - 提供用户级熔断接口(通过handler提前return实现) |
| 58 | +
|
| 59 | + - 性能考量 : |
58 | 60 |
|
59 | | - - 性能考量 : |
| 61 | + - 采用"尽力而为"策略处理异常连接的任务 |
| 62 | + |
| 63 | + - 通过wg.Wait()保证已接收任务执行完毕 |
| 64 | + |
| 65 | + - 优雅关闭时确保: |
60 | 66 |
|
61 | | - - 采用"尽力而为"策略处理异常连接的任务 |
| 67 | + - reader先行退出 |
62 | 68 | |
63 | | - - 通过wg.Wait()保证已接收任务执行完毕 |
| 69 | + - writer处理完消息队列 |
64 | 70 | |
65 | | - - 优雅关闭时确保: |
66 | | - |
67 | | - - reader先行退出 |
68 | | - |
69 | | - - writer处理完消息队列 |
70 | | - |
71 | | - - 管道关闭时无残留数据 |
| 71 | + - 管道关闭时无残留数据 |
72 | 72 |
|
73 | 73 | 经验总结: |
74 | 74 |
|
75 | | - - 协程同步应优先使用简单同步原语 |
76 | | - |
77 | | - - 资源释放需保证执行完整性 |
78 | | - |
79 | | - - 异常处理应区分系统级和业务级容错 |
80 | | - |
81 | | - - 避免过度设计,当前方案在代码复杂度和功能需求间取得平衡 |
| 75 | + - 协程同步应优先使用简单同步原语 |
| 76 | + |
| 77 | + - 资源释放需保证执行完整性 |
| 78 | + |
| 79 | + - 异常处理应区分系统级和业务级容错 |
| 80 | + |
| 81 | + - 避免过度设计,当前方案在代码复杂度和功能需求间取得平衡 |
82 | 82 |
|
83 | 83 | 3. 服务端异步启动后 stop 在大量并发连接下的死锁问题 |
84 | 84 |
|
85 | 85 | 现象:服务端在异步启动后调用 Stop 方法时,面对大量并发连接场景,程序因死锁异常退出。 |
86 | 86 | |
87 | 87 | 问题本质 : |
88 | 88 |
|
89 | | - - 锁竞争与链式调用 : |
| 89 | + - 锁竞争与链式调用 : |
90 | 90 |
|
91 | | - - 原connmanager模块使用sync.Mutex保护连接集合(map),Stop操作需遍历所有连并逐个调用conn.Stop()。 |
92 | | - |
93 | | - - conn.Stop()内部可能再次申请锁(如连接自身的状态锁),导致嵌套锁竞争 。 |
| 91 | + - 原connmanager模块使用sync.Mutex保护连接集合(map),Stop操作需遍历所有连并逐个调用conn.Stop()。 |
| 92 | + |
| 93 | + - conn.Stop()内部可能再次申请锁(如连接自身的状态锁),导致嵌套锁竞争 。 |
94 | 94 |
|
95 | | - - 当connmanager的锁未释放时,调用conn.Stop()可能因等待连接内部锁而阻塞,形成死锁链。 |
96 | | - - 资源清理顺序缺陷 : |
97 | | - |
98 | | - - 未明确资源释放的优先级,导致连接关闭时可能仍在处理旧任务或接收新请求。 |
| 95 | + - 当connmanager的锁未释放时,调用conn.Stop()可能因等待连接内部锁而阻塞,形成死锁链。 |
| 96 | + - 资源清理顺序缺陷 : |
| 97 | + |
| 98 | + - 未明确资源释放的优先级,导致连接关闭时可能仍在处理旧任务或接收新请求。 |
99 | 99 | |
100 | 100 | 解决方案 : |
101 | 101 |
|
102 | | - - 数据结构优化 : |
103 | | - - 替换为sync.Map : |
104 | | - 将连接集合从普通map+Mutex替换为sync.Map,利用其无锁并发特性,避免遍历时的锁竞争。 |
105 | | - - 原子状态管理 : |
106 | | - 使用atomic包标记服务端状态(如isRunning),确保状态变更的原子性,避免竞态。 |
| 102 | + - 数据结构优化 : |
| 103 | + - 替换为sync.Map : |
| 104 | + 将连接集合从普通map+Mutex替换为sync.Map,利用其无锁并发特性,避免遍历时的锁竞争。 |
| 105 | + - 原子状态管理 : |
| 106 | + 使用atomic包标记服务端状态,确保状态变更的原子性,避免竞态。 |
107 | 107 | |
108 | | - 流程控制优化 : |
109 | | - ``` |
110 | | - // 服务端Stop流程 |
111 | | - func (s *Server) Stop() { |
112 | | - atomic.StoreInt32(&s.isRunning, 0) // 原子标记停止状态 |
113 | | - s.connManager.Range(func(conn Conn) { |
114 | | - conn.Stop() // 并发安全地逐个关闭连接 |
115 | | - }) |
116 | | - // 等待所有连接关闭完成(通过WaitGroup或通道通知) |
117 | | - } |
118 | | - ``` |
119 | | - - 连接关闭一致性 : |
120 | | -拒绝新连接 :在Stop开始时立即关闭监听套接字。 |
121 | | -处理历史任务 :确保已接收的任务执行完毕(如通过sync.WaitGroup跟踪任务)。 |
122 | | -优雅退出 : |
123 | | -先关闭读协程(reader),停止接收新数据。 |
124 | | -等待写协程(writer)处理完消息队列。 |
125 | | -最后清理资源(如关闭通道)。 |
126 | | -
|
127 | | -经验总结 : |
128 | | -
|
129 | | -避免嵌套锁 :优先使用并发安全的数据结构(如sync.Map)减少锁粒度。 |
130 | | -资源生命周期管理 :明确资源创建、使用、销毁的顺序,避免循环依赖。 |
131 | | -优雅退出设计 : |
132 | | -分阶段关闭(先停读,再停写,最后清理)。 |
133 | | -通过原子状态和WaitGroup确保执行完整性。 |
134 | | -压测验证必要性 :高并发场景必须通过压力测试暴露隐藏的竞态和死锁问题。 |
| 108 | + 经验总结 : |
| 109 | +
|
| 110 | + - 避免嵌套锁 :优先使用并发安全的数据结构(如sync.Map)减少锁粒度。 |
| 111 | + - 资源生命周期管理 :明确资源创建、使用、销毁的顺序,避免循环依赖。 |
| 112 | + - 优雅退出设计 : |
| 113 | + - 分阶段关闭(先停读,再停写,最后清理)。 |
| 114 | + - 通过原子状态和WaitGroup确保执行完整性。 |
| 115 | + - 压测验证必要性 :高并发场景必须通过压力测试暴露隐藏的竞态和死锁问题。 |
0 commit comments