feat(notify): 重构通信内核并补齐 stream/bulk/record/transfer 能力
- 引入 LogicalConn/TransportConn 分层,ClientConn 保留兼容适配层 - 新增 Stream、Bulk、RecordStream 三条数据面能力及对应控制路径 - 完成 transfer/file 传输内核与状态快照、诊断能力 - 补齐 reconnect、inbound dispatcher、modern psk 等基础模块 - 增加大规模回归、并发与基准测试覆盖 - 更新依赖库
This commit is contained in:
+177
@@ -0,0 +1,177 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errFileAckCanceled = errors.New("file ack canceled")
|
||||
errFileAckTimeout = errors.New("file ack timeout")
|
||||
)
|
||||
|
||||
type fileAckWait struct {
|
||||
key string
|
||||
scope string
|
||||
pool *fileAckPool
|
||||
reply chan FileEvent
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
type fileAckPool struct {
|
||||
pool sync.Map
|
||||
}
|
||||
|
||||
func newFileAckPool() *fileAckPool {
|
||||
return &fileAckPool{}
|
||||
}
|
||||
|
||||
func fileAckKey(scope string, fileID string, stage string, offset int64) string {
|
||||
return normalizeFileScope(scope) + "|" + fileID + "|" + stage + "|" + formatInt(offset)
|
||||
}
|
||||
|
||||
func (p *fileAckPool) prepare(scope string, fileID string, stage string, offset int64) *fileAckWait {
|
||||
scope = normalizeFileScope(scope)
|
||||
wait := &fileAckWait{
|
||||
key: fileAckKey(scope, fileID, stage, offset),
|
||||
scope: scope,
|
||||
pool: p,
|
||||
reply: make(chan FileEvent, 1),
|
||||
}
|
||||
p.pool.Store(wait.key, wait)
|
||||
return wait
|
||||
}
|
||||
|
||||
func (p *fileAckPool) deliver(scope string, event FileEvent) bool {
|
||||
return p.deliverAny([]string{scope}, event)
|
||||
}
|
||||
|
||||
func (p *fileAckPool) deliverAny(scopes []string, event FileEvent) bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
for _, scope := range scopes {
|
||||
key := fileAckKey(scope, event.Packet.FileID, event.Packet.Stage, event.Packet.Offset)
|
||||
data, ok := p.pool.LoadAndDelete(key)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
wait := data.(*fileAckWait)
|
||||
wait.deliver(event)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *fileAckWait) cancel() {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
if w.pool != nil {
|
||||
w.pool.pool.Delete(w.key)
|
||||
}
|
||||
w.closeReply()
|
||||
}
|
||||
|
||||
func (w *fileAckWait) deliver(event FileEvent) {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
w.closeOnce.Do(func() {
|
||||
select {
|
||||
case w.reply <- event:
|
||||
default:
|
||||
}
|
||||
close(w.reply)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *fileAckWait) closeReply() {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
w.closeOnce.Do(func() {
|
||||
close(w.reply)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *fileAckPool) waitPrepared(wait *fileAckWait, timeout time.Duration) error {
|
||||
if timeout <= 0 {
|
||||
timeout = defaultFileAckTimeout
|
||||
}
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case event, ok := <-wait.reply:
|
||||
if !ok {
|
||||
return errFileAckCanceled
|
||||
}
|
||||
if event.Err != nil {
|
||||
return event.Err
|
||||
}
|
||||
if event.Packet.Error != "" {
|
||||
return errors.New(event.Packet.Error)
|
||||
}
|
||||
return nil
|
||||
case <-timer.C:
|
||||
wait.cancel()
|
||||
return errFileAckTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fileAckPool) wait(scope string, fileID string, stage string, offset int64, timeout time.Duration) error {
|
||||
wait := p.prepare(scope, fileID, stage, offset)
|
||||
return p.waitPrepared(wait, timeout)
|
||||
}
|
||||
|
||||
func (p *fileAckPool) closeAll() {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
p.pool.Range(func(_, value interface{}) bool {
|
||||
value.(*fileAckWait).cancel()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (p *fileAckPool) closeScope(scope string) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
scope = normalizeFileScope(scope)
|
||||
p.pool.Range(func(_, value interface{}) bool {
|
||||
wait := value.(*fileAckWait)
|
||||
if wait.scope == scope {
|
||||
wait.cancel()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (p *fileAckPool) closeScopeFamily(scope string) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
base := normalizeFileScope(scope)
|
||||
p.pool.Range(func(_, value interface{}) bool {
|
||||
wait := value.(*fileAckWait)
|
||||
if scopeBelongsToServerFileScope(wait.scope, base) {
|
||||
wait.cancel()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func formatInt(v int64) string {
|
||||
return strconv.FormatInt(v, 10)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) getFileAckPool() *fileAckPool {
|
||||
return c.getLogicalSessionState().fileAckWaits
|
||||
}
|
||||
|
||||
func (s *ServerCommon) getFileAckPool() *fileAckPool {
|
||||
return s.getLogicalSessionState().fileAckWaits
|
||||
}
|
||||
Reference in New Issue
Block a user