fix(notify): 根治低带宽控制面阻塞与传输写入卡死
- 为控制消息增加优先级、公平调度、队列字节预算和自适应批处理 - 支持可取消的写门等待,收紧 shared/dedicated bulk、stream 和 Reply 写入边界 - 修复 bulk reset/close、连接 handoff 和安全 profile 切换时序 - 保留旧取消与超时错误契约,新增阶段化 TransportSendError - 增加 ReplyCtx、ReplyObjCtx、写超时配置及黑洞连接和竞态回归测试
This commit is contained in:
+124
-14
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user