来自 #130 的架构级评审建议。不阻塞合入,仅供参考是否有更好的架构解法。
⚠️ [重要 · 错误处理] PreHandle 丢弃时回写响应可能在连接已关闭时调用 SendBuffMsg service/router.go:42
问题根因:Router.PreHandle 在 action == banIface.HookDrop 时调用 sendDropped(request),进而调用 req.GetConnection().SendBuffMsg(...)。如果该连接在钩子执行期间已被关闭(例如客户端断开、超时踢掉),SendBuffMsg 会在一个已关闭的连接上写数据。框架层面如何处理这种情况?如果 connection.go 的 SendBuffMsg 在连接已关闭时 panic 或阻塞,就会导致 goroutine 泄漏或未捕获的 panic。
为什么低级解法不够:在 sendDropped 前检查连接状态或加 recover 是临时解法——不能解决连接对象已被回收或 chan 已关闭的根本问题。
架构级方案:让 SendBuffMsg 在连接关闭时安全地返回错误(已经关闭的 chan 上 send 会 panic,需要用 select+default 或 recover 处理)。Router.PreHandle 应忽略 sendDropped 的错误——因为被丢弃的帧如果连响应都送不回去,客户端已经断开,丢弃的语义仍然成立。这需要在连接层提供安全的写 API(如 TrySend 返回 error)。
代价/收益:代价:需要在连接层增加安全发送的机制(select+default 或 closed 检测)。收益:避免 goroutine panic 导致整个 worker 池进程崩溃。
service/router.go:42问题根因:
Router.PreHandle在action == banIface.HookDrop时调用sendDropped(request),进而调用req.GetConnection().SendBuffMsg(...)。如果该连接在钩子执行期间已被关闭(例如客户端断开、超时踢掉),SendBuffMsg会在一个已关闭的连接上写数据。框架层面如何处理这种情况?如果connection.go的SendBuffMsg在连接已关闭时 panic 或阻塞,就会导致 goroutine 泄漏或未捕获的 panic。为什么低级解法不够:在 sendDropped 前检查连接状态或加 recover 是临时解法——不能解决连接对象已被回收或 chan 已关闭的根本问题。
架构级方案:让
SendBuffMsg在连接关闭时安全地返回错误(已经关闭的 chan 上 send 会 panic,需要用 select+default 或 recover 处理)。Router.PreHandle 应忽略sendDropped的错误——因为被丢弃的帧如果连响应都送不回去,客户端已经断开,丢弃的语义仍然成立。这需要在连接层提供安全的写 API(如TrySend返回 error)。代价/收益:代价:需要在连接层增加安全发送的机制(select+default 或 closed 检测)。收益:避免 goroutine panic 导致整个 worker 池进程崩溃。