fix(notify): 根治低带宽控制面阻塞与传输写入卡死

- 为控制消息增加优先级、公平调度、队列字节预算和自适应批处理
- 支持可取消的写门等待,收紧 shared/dedicated bulk、stream 和 Reply 写入边界
- 修复 bulk reset/close、连接 handoff 和安全 profile 切换时序
- 保留旧取消与超时错误契约,新增阶段化 TransportSendError
- 增加 ReplyCtx、ReplyObjCtx、写超时配置及黑洞连接和竞态回归测试
This commit is contained in:
2026-08-14 10:16:37 +08:00
parent 98ef9e7fcc
commit 0826e17063
45 changed files with 4231 additions and 293 deletions
+124 -14
View File
@@ -2,6 +2,7 @@ package notify
import (
"b612.me/stario"
"context"
"errors"
"io"
"net"
@@ -10,9 +11,15 @@ import (
"time"
)
var transportConnWriteLocks sync.Map
var transportConnWriteGates sync.Map
var errTransportFrameQueueUnavailable = errors.New("transport frame queue is unavailable")
type connWriteGateRef struct {
mu sync.Mutex
gate chan struct{}
refs int
}
type vectoredBuffersWriter interface {
WriteBuffers(*net.Buffers) (int64, error)
}
@@ -124,33 +131,136 @@ func withRawConnWriteLock(conn net.Conn, fn func(net.Conn) error) error {
}
func withRawConnWriteLockDeadline(conn net.Conn, deadline time.Time, fn func(net.Conn) error) error {
_, err := withRawConnWriteLockContextDeadline(context.Background(), conn, deadline, fn)
return err
}
func withRawConnWriteLockContextDeadline(ctx context.Context, conn net.Conn, deadline time.Time, fn func(net.Conn) error) (bool, error) {
if conn == nil {
return net.ErrClosed
return false, net.ErrClosed
}
lock := rawConnWriteLock(conn)
lock.Lock()
defer lock.Unlock()
if ctx == nil {
ctx = context.Background()
}
gateRef := retainRawConnWriteGate(conn)
defer releaseRawConnWriteGate(conn, gateRef)
gate := gateRef.gate
if err := lockWriteGateContextDeadline(ctx, nil, gate, deadline); err != nil {
return false, err
}
defer func() { gate <- struct{}{} }()
if err := ctx.Err(); err != nil {
return false, err
}
deadline = earlierWriteDeadline(deadline, contextDeadline(ctx))
if !deadline.IsZero() {
if err := conn.SetWriteDeadline(deadline); err != nil {
return err
return true, err
}
defer func() {
_ = conn.SetWriteDeadline(time.Time{})
}()
}
return fn(conn)
return true, fn(conn)
}
func rawConnWriteLock(conn net.Conn) *sync.Mutex {
func retainRawConnWriteGate(conn net.Conn) *connWriteGateRef {
if conn == nil {
return &sync.Mutex{}
return &connWriteGateRef{gate: newConnWriteGate(), refs: 1}
}
if lock, ok := transportConnWriteLocks.Load(conn); ok {
return lock.(*sync.Mutex)
for {
candidate := &connWriteGateRef{gate: newConnWriteGate(), refs: 1}
actual, loaded := transportConnWriteGates.LoadOrStore(conn, candidate)
if !loaded {
return candidate
}
ref := actual.(*connWriteGateRef)
ref.mu.Lock()
if ref.refs > 0 {
ref.refs++
ref.mu.Unlock()
return ref
}
ref.mu.Unlock()
transportConnWriteGates.CompareAndDelete(conn, ref)
}
lock := &sync.Mutex{}
actual, _ := transportConnWriteLocks.LoadOrStore(conn, lock)
return actual.(*sync.Mutex)
}
func releaseRawConnWriteGate(conn net.Conn, ref *connWriteGateRef) {
if ref == nil {
return
}
ref.mu.Lock()
if ref.refs > 0 {
ref.refs--
}
remove := ref.refs == 0
ref.mu.Unlock()
if remove && conn != nil {
transportConnWriteGates.CompareAndDelete(conn, ref)
}
}
func newConnWriteGate() chan struct{} {
gate := make(chan struct{}, 1)
gate <- struct{}{}
return gate
}
func lockWriteGateContextDeadline(ctx context.Context, stop <-chan struct{}, gate chan struct{}, deadline time.Time) error {
if ctx == nil {
ctx = context.Background()
}
select {
case <-ctx.Done():
return ctx.Err()
case <-stop:
return net.ErrClosed
default:
}
select {
case <-gate:
return nil
default:
}
if deadline.IsZero() {
select {
case <-ctx.Done():
return ctx.Err()
case <-stop:
return net.ErrClosed
case <-gate:
return nil
}
}
wait := time.Until(deadline)
if wait <= 0 {
return context.DeadlineExceeded
}
timer := time.NewTimer(wait)
defer timer.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-stop:
return net.ErrClosed
case <-timer.C:
return context.DeadlineExceeded
case <-gate:
return nil
}
}
func shorterPositiveDuration(left time.Duration, right time.Duration) time.Duration {
left = maxDuration(0, left)
right = maxDuration(0, right)
if left == 0 {
return right
}
if right == 0 || left < right {
return left
}
return right
}
func writeFramedPayloadUnlocked(conn net.Conn, queue *stario.StarQueue, payload []byte) error {