notify/bulk_dispatcher.go
starainrt 09d972c7b7
feat(notify): 重构通信内核并补齐 stream/bulk/record/transfer 能力
- 引入 LogicalConn/TransportConn 分层,ClientConn 保留兼容适配层
  - 新增 Stream、Bulk、RecordStream 三条数据面能力及对应控制路径
  - 完成 transfer/file 传输内核与状态快照、诊断能力
  - 补齐 reconnect、inbound dispatcher、modern psk 等基础模块
  - 增加大规模回归、并发与基准测试覆盖
  - 更新依赖库
2026-04-15 15:24:36 +08:00

156 lines
4.4 KiB
Go

package notify
import (
"context"
"errors"
"fmt"
"io"
"net"
"time"
)
const bulkDispatchRejectTimeout = 300 * time.Millisecond
func (c *ClientCommon) dispatchFastBulkFrame(frame bulkFastFrame) {
if frame.DataID == 0 {
return
}
runtime := c.getBulkRuntime()
if runtime == nil {
return
}
bulk, ok := runtime.lookupByDataID(clientFileScope(), frame.DataID)
if !ok {
if c.showError || c.debugMode {
fmt.Println("client bulk data for unknown data id", frame.DataID)
}
c.bestEffortRejectInboundBulkData("", frame.DataID, errBulkNotFound.Error())
return
}
if !bulk.acceptsClientSessionEpoch(c.currentClientSessionEpoch()) {
if c.showError || c.debugMode {
fmt.Println("client bulk data rejected by stale session epoch", frame.DataID)
}
detachErr := transportDetachedSessionEpochError()
bulk.markReset(detachErr)
c.bestEffortRejectInboundBulkData(bulk.ID(), frame.DataID, detachErr.Error())
return
}
switch frame.Type {
case bulkFastPayloadTypeData:
if err := bulk.pushOwnedChunk(frame.Payload); err != nil {
if c.showError || c.debugMode {
fmt.Println("client bulk push chunk error", err)
}
if !errors.Is(err, io.EOF) {
c.bestEffortRejectInboundBulkData(bulk.ID(), frame.DataID, err.Error())
}
}
case bulkFastPayloadTypeClose:
if frame.Flags&bulkFastPayloadFlagFullClose != 0 {
bulk.markPeerClosed()
return
}
bulk.markRemoteClosed()
case bulkFastPayloadTypeReset:
resetErr := errBulkReset
if len(frame.Payload) > 0 {
resetErr = bulkRemoteResetError(string(frame.Payload))
}
bulk.markReset(bulkResetError(resetErr))
}
}
func (c *ClientCommon) dispatchFastBulkData(frame bulkFastDataFrame) {
c.dispatchFastBulkFrame(frame)
}
func (s *ServerCommon) dispatchFastBulkFrame(logical *LogicalConn, transport *TransportConn, conn net.Conn, frame bulkFastFrame) {
if logical == nil || frame.DataID == 0 {
return
}
runtime := s.getBulkRuntime()
if runtime == nil {
return
}
bulk, ok := runtime.lookupByDataID(serverFileScope(logical), frame.DataID)
if !ok {
if s.showError || s.debugMode {
fmt.Println("server bulk data for unknown data id", frame.DataID)
}
s.bestEffortRejectInboundBulkData(logical, transport, conn, "", frame.DataID, errBulkNotFound.Error())
return
}
if !bulk.acceptsTransportGeneration(transport) {
if s.showError || s.debugMode {
fmt.Println("server bulk data rejected by transport generation mismatch", frame.DataID)
}
detachErr := transportDetachedGenerationMismatchError(bulk.TransportGeneration(), transport)
s.bestEffortRejectInboundBulkData(logical, transport, conn, bulk.ID(), frame.DataID, detachErr.Error())
return
}
switch frame.Type {
case bulkFastPayloadTypeData:
if err := bulk.pushOwnedChunk(frame.Payload); err != nil {
if s.showError || s.debugMode {
fmt.Println("server bulk push chunk error", err)
}
if !errors.Is(err, io.EOF) {
s.bestEffortRejectInboundBulkData(logical, transport, conn, bulk.ID(), frame.DataID, err.Error())
}
}
case bulkFastPayloadTypeClose:
if frame.Flags&bulkFastPayloadFlagFullClose != 0 {
bulk.markPeerClosed()
return
}
bulk.markRemoteClosed()
case bulkFastPayloadTypeReset:
resetErr := errBulkReset
if len(frame.Payload) > 0 {
resetErr = bulkRemoteResetError(string(frame.Payload))
}
bulk.markReset(bulkResetError(resetErr))
}
}
func (s *ServerCommon) dispatchFastBulkData(logical *LogicalConn, transport *TransportConn, conn net.Conn, frame bulkFastDataFrame) {
s.dispatchFastBulkFrame(logical, transport, conn, frame)
}
func (c *ClientCommon) bestEffortRejectInboundBulkData(bulkID string, dataID uint64, message string) {
if c == nil || (bulkID == "" && dataID == 0) {
return
}
ctx, cancel := context.WithTimeout(context.Background(), bulkDispatchRejectTimeout)
defer cancel()
_, _ = sendBulkResetClient(ctx, c, BulkResetRequest{
BulkID: bulkID,
DataID: dataID,
Error: message,
})
}
func (s *ServerCommon) bestEffortRejectInboundBulkData(logical *LogicalConn, transport *TransportConn, conn net.Conn, bulkID string, dataID uint64, message string) {
if s == nil || logical == nil || (bulkID == "" && dataID == 0) {
return
}
payload, err := encode(BulkResetRequest{
BulkID: bulkID,
DataID: dataID,
Error: message,
})
if err != nil {
return
}
env, err := wrapTransferMsgEnvelope(TransferMsg{
Key: BulkResetSignalKey,
Value: payload,
Type: MSG_ASYNC,
}, s.sequenceEn)
if err != nil {
return
}
_ = s.sendEnvelopeInboundTransport(logical, transport, conn, env)
}