fix(notify): 修复传输生命周期竞态,完善背压与协议边界

- 完善 stream/bulk DataID 分配、预留和双向命名空间,修复并发打开及 dedicated/shared 回退时的 ID 冲突
- 将收发、回复、恢复任务和 sidecar 绑定原始会话与物理连接,防止重连后的旧消息误操作新连接
- 加强 close/reset 身份校验及实例移除检查,修复 dedicated attach 失败、通道引用和资源回收竞态
- 收紧批量发送器停止准入,确保在途入队完成后统一清理请求、缓冲区和等待者
- 修复 record 满队列死锁、取消时序号消耗及关闭竞态,确保关闭有界并返回真实错误
- 增加协商式 record 逻辑半关闭,保留反向 ACK;通过 reset 传递 RecordFailure,避免背压掩盖原始失败原因
- 补齐帧长度、批次数量、序号溢出和未确认窗口校验,提前拒绝超限数据并按字节预算拆批
- 为入站分发增加全局及单连接的条数、字节预算和阻塞背压,关闭时唤醒等待者,消除正常断连日志噪音
- 完善 bulk 窗口释放失败处理与传输诊断,补充并发、重连、背压、协议边界及真实 TCP 回归覆盖
This commit is contained in:
2026-09-23 15:33:17 +08:00
parent 0826e17063
commit 1f2e74acca
79 changed files with 9190 additions and 1013 deletions
+26 -8
View File
@@ -5,7 +5,6 @@ import (
"context"
"errors"
"fmt"
"math"
"net"
"sync/atomic"
"time"
@@ -286,7 +285,7 @@ func (c *ClientCommon) startClientWithConn(conn net.Conn) error {
func (c *ClientCommon) startClientWithConnSource(conn net.Conn, source *clientConnectSource) error {
stopCtx, stopFn := context.WithCancel(context.Background())
epoch := c.beginClientSessionEpoch()
queue := stario.NewQueueCtx(stopCtx, 4, math.MaxUint32)
queue := stario.NewQueueCtx(stopCtx, 4, transportFrameMaxPayloadBytes)
c.setClientConnectSource(source)
rt := newClientSessionRuntime(conn, stopCtx, stopFn, queue, epoch)
c.setClientSessionRuntimeWithCloseOld(rt, true)
@@ -341,7 +340,7 @@ func (c *ClientCommon) startClientTransportRuntime(rt *clientSessionRuntime) err
if c.useHeartBeat {
go c.heartbeatLoop(transportStopCtx, rt.epoch)
}
go c.readMessageLoop(transportStopCtx, rt.conn, rt.queue, rt.epoch)
go c.readMessageLoopAtRoute(transportStopCtx, clientSessionRouteFromRuntime(rt))
go c.loadMessageLoop(rt)
return nil
}
@@ -440,10 +439,28 @@ func (c *ClientCommon) readMessage() {
}
func (c *ClientCommon) readMessageLoop(stopCtx context.Context, conn net.Conn, queue *stario.StarQueue, epoch uint64) {
route := c.clientSessionRouteSnapshot()
if route.binding == nil || route.binding.connSnapshot() != conn || route.binding.queueSnapshot() != queue || route.epoch != epoch {
route = clientSessionRoute{
binding: newTransportBinding(conn, queue),
epoch: epoch,
sessionStopCtx: stopCtx,
transportStopCtx: stopCtx,
}
}
c.readMessageLoopAtRoute(stopCtx, route)
}
func (c *ClientCommon) readMessageLoopAtRoute(stopCtx context.Context, route clientSessionRoute) {
if stopCtx == nil {
return
}
binding := newTransportBinding(conn, queue)
binding := route.binding
if binding == nil {
return
}
conn := binding.connSnapshot()
queue := binding.queueSnapshot()
dispatcher := c.clientInboundDispatcherSnapshot()
if conn != nil && queue != nil && !isPacketTransportConn(conn) {
reader := newTransportFrameReader(conn, queue)
@@ -455,7 +472,7 @@ func (c *ClientCommon) readMessageLoop(stopCtx context.Context, conn net.Conn, q
default:
}
payload, release, err := c.readTransportPayloadPooled(conn, reader)
if !c.handleTransportPayloadReadResultWithSession(stopCtx, binding, payload, release, err, epoch, dispatcher) {
if !c.handleTransportPayloadReadResultAtRoute(stopCtx, route, payload, release, err, dispatcher) {
return
}
}
@@ -469,7 +486,7 @@ func (c *ClientCommon) readMessageLoop(stopCtx context.Context, conn net.Conn, q
default:
}
readNum, data, err := c.readFromTransportBindingWithBuffer(binding, buf)
if !c.handleTransportReadResultWithSessionDispatcher(stopCtx, conn, queue, readNum, data, err, epoch, dispatcher) {
if !c.handleTransportReadResultAtRoute(stopCtx, route, readNum, data, err, dispatcher) {
return
}
}
@@ -512,6 +529,7 @@ func (c *ClientCommon) loadMessageLoop(rt *clientSessionRuntime) {
return
}
dispatcher := rt.inboundDispatcher
route := clientSessionRouteFromRuntime(rt)
if dispatcher == nil {
dispatcher = newInboundDispatcher()
defer dispatcher.CloseAndWait()
@@ -534,10 +552,10 @@ func (c *ClientCommon) loadMessageLoop(rt *clientSessionRuntime) {
}
msg := data
c.wg.Add(1)
if !dispatcher.Dispatch(clientInboundDispatchSource(), func() {
if !dispatcher.DispatchSized(clientInboundDispatchSource(), len(msg.Msg), func() {
defer c.wg.Done()
now := time.Now()
if err := c.dispatchInboundTransportPayload(msg.Msg, now); err != nil {
if err := c.dispatchInboundTransportPayloadAtRoute(route, msg.Msg, now); err != nil {
if c.showError || c.debugMode {
fmt.Println("client decode envelope error", err)
}