fix: 停机排空计入正在发送的帧并为断开预留时间

This commit is contained in:
Nixevol
2026-09-30 19:29:37 +08:00
parent a7a6e34387
commit ca3434421b
4 changed files with 41 additions and 7 deletions
+16 -6
View File
@@ -142,6 +142,7 @@ type connState struct {
downDone chan struct{}
downBytes atomic.Int64
wirePending atomic.Int64 // Publish 入 mochi outbound 后、OnPacketSent 前
inSend atomic.Int32 // downLoop 已取出帧、尚未从 sendOne 返回
mu sync.Mutex
// 带断开的下行帧:只等本帧 OnPacketSent,不用连接级计数。
@@ -237,8 +238,8 @@ func (b *Broker) Close() error {
}
// Shutdown 向所有连接发 MQTT 5 0x8B 后关闭。完整 HTTP 停机顺序见 L-03。
// ctx 未取消时先等下行队列与 wirePending 排空,再 DisconnectClient(此时 outbound 空,
// 0x8B 直写套接字),并在截止前等连接拆掉;ctx 已取消则发完即 Close,不等待。
// ctx 未取消时先等下行队列、正在 sendOne 的帧与 wirePending 排空(若有截止时间则预留约 1 秒),
// 再 DisconnectClient,并在截止前等连接拆掉;ctx 已取消则发完即 Close,不等待。
func (b *Broker) Shutdown(ctx context.Context) error {
if b.closed.Load() {
return nil
@@ -275,17 +276,26 @@ func (b *Broker) Shutdown(ctx context.Context) error {
var waitErr error
if !alreadyCancelled {
if !b.waitConnsQuiet(ctx, states) {
// 留出约 1 秒给 0x8B 写出,避免排空把整个截止时间用完后立刻 Close。
quietCtx := ctx
cancelQuiet := func() {}
if dl, ok := ctx.Deadline(); ok && time.Until(dl) > time.Second {
quietCtx, cancelQuiet = context.WithDeadline(ctx, dl.Add(-time.Second))
}
if !b.waitConnsQuiet(quietCtx, states) && ctx.Err() != nil {
waitErr = ctx.Err()
}
cancelQuiet()
}
for _, cl := range clients {
_ = b.server.DisconnectClient(cl, packets.ErrServerShuttingDown)
}
if !alreadyCancelled && waitErr == nil {
waitErr = b.waitConnsGone(ctx)
if !alreadyCancelled && ctx.Err() == nil {
if err := b.waitConnsGone(ctx); err != nil {
waitErr = err
}
}
closeErr := b.Close()
@@ -299,7 +309,7 @@ func (b *Broker) waitConnsQuiet(ctx context.Context, states []*connState) bool {
for {
quiet := true
for _, st := range states {
if st.wirePending.Load() > 0 {
if st.inSend.Load() > 0 || st.wirePending.Load() > 0 {
quiet = false
break
}