Files
synapse/internal/components/harbor/harbor.go
2026-06-28 03:24:26 +08:00

522 lines
12 KiB
Go

// Package harbor 灵泊 - 异步队列与暂存层
//
// "泊"为船舶停靠。数据包如远行之舟,
// 遇风浪(网络抖动)时暂泊于此,
// 待风平浪静(目标就绪)再启航。
// 不丢包、不超时,给予 Agent 异步协作的从容。
package harbor
import (
"context"
"encoding/json"
"fmt"
"log"
"sync"
"time"
"synapse/pkg/types"
)
// =============================================================================
// Message Queue - 消息队列
// =============================================================================
// QueueType 队列类型
type QueueType string
const (
QueueFIFO QueueType = "fifo" // 先进先出
QueuePriority QueueType = "priority" // 优先级队列
QueueLIFO QueueType = "lifo" // 后进先出
)
// QueuedMessage 队列中的消息
type QueuedMessage struct {
ID string
Message *types.UnifiedSynapseMessage
Priority int
EnqueueTime time.Time
RetryCount int
MaxRetries int
NextRetry time.Time
Status QueuedStatus
Target string // 目标 endpoint
}
// QueuedStatus 队列消息状态
type QueuedStatus string
const (
QueuedPending QueuedStatus = "pending"
QueuedProcessing QueuedStatus = "processing"
QueuedDelivered QueuedStatus = "delivered"
QueuedFailed QueuedStatus = "failed"
QueuedDead QueuedStatus = "dead"
)
// MessageQueue 消息队列
type MessageQueue struct {
mu sync.RWMutex
messages []*QueuedMessage
config *QueueConfig
}
// QueueConfig 队列配置
type QueueConfig struct {
MaxSize int // 最大队列大小
DefaultMaxRetries int // 默认最大重试次数
DefaultRetryDelay time.Duration // 默认重试延迟
}
// DefaultQueueConfig 返回默认队列配置
func DefaultQueueConfig() *QueueConfig {
return &QueueConfig{
MaxSize: 10000,
DefaultMaxRetries: 5,
DefaultRetryDelay: 30 * time.Second,
}
}
// NewMessageQueue 创建消息队列
func NewMessageQueue(config *QueueConfig) *MessageQueue {
if config == nil {
config = DefaultQueueConfig()
}
return &MessageQueue{
messages: make([]*QueuedMessage, 0),
config: config,
}
}
// Enqueue 入队
func (mq *MessageQueue) Enqueue(msg *types.UnifiedSynapseMessage, target string, priority int) (*QueuedMessage, error) {
mq.mu.Lock()
defer mq.mu.Unlock()
if len(mq.messages) >= mq.config.MaxSize {
return nil, fmt.Errorf("queue is full")
}
qm := &QueuedMessage{
ID: fmt.Sprintf("q-%d", time.Now().UnixNano()),
Message: msg,
Priority: priority,
EnqueueTime: time.Now(),
RetryCount: 0,
MaxRetries: mq.config.DefaultMaxRetries,
NextRetry: time.Now(),
Status: QueuedPending,
Target: target,
}
// 按优先级插入
inserted := false
for i, m := range mq.messages {
if priority > m.Priority {
mq.messages = append(mq.messages[:i], append([]*QueuedMessage{qm}, mq.messages[i:]...)...)
inserted = true
break
}
}
if !inserted {
mq.messages = append(mq.messages, qm)
}
log.Printf("[灵泊] 消息入队: %s (目标: %s, 优先级: %d)", qm.ID, target, priority)
return qm, nil
}
// Dequeue 出队
func (mq *MessageQueue) Dequeue() (*QueuedMessage, error) {
mq.mu.Lock()
defer mq.mu.Unlock()
now := time.Now()
for i, qm := range mq.messages {
if qm.Status == QueuedPending && now.After(qm.NextRetry) {
qm.Status = QueuedProcessing
// 移到待处理
mq.messages = append(mq.messages[:i], mq.messages[i+1:]...)
return qm, nil
}
}
return nil, fmt.Errorf("no messages available")
}
// MarkDelivered 标记为已送达
func (mq *MessageQueue) MarkDelivered(msgID string) error {
mq.mu.Lock()
defer mq.mu.Unlock()
for _, qm := range mq.messages {
if qm.ID == msgID {
qm.Status = QueuedDelivered
log.Printf("[灵泊] 消息送达: %s", msgID)
return nil
}
}
return fmt.Errorf("message %s not found", msgID)
}
// MarkFailed 标记为失败(会重试)
func (mq *MessageQueue) MarkFailed(msgID string, err error) error {
mq.mu.Lock()
defer mq.mu.Unlock()
for _, qm := range mq.messages {
if qm.ID == msgID {
qm.RetryCount++
if qm.RetryCount >= qm.MaxRetries {
qm.Status = QueuedDead
log.Printf("[灵泊] 消息失败(已死): %s (重试: %d次)", msgID, qm.RetryCount)
} else {
qm.Status = QueuedPending
// 指数退避
delay := mq.config.DefaultRetryDelay * time.Duration(1<<uint(qm.RetryCount-1))
qm.NextRetry = time.Now().Add(delay)
log.Printf("[灵泊] 消息失败(将重试): %s (重试: %d/%d, 下次: %v)",
msgID, qm.RetryCount, qm.MaxRetries, delay)
}
return nil
}
}
return fmt.Errorf("message %s not found", msgID)
}
// Size 队列大小
func (mq *MessageQueue) Size() int {
mq.mu.RLock()
defer mq.mu.RUnlock()
return len(mq.messages)
}
// =============================================================================
// Message Buffer - 消息缓冲
// =============================================================================
// MessageBuffer 消息缓冲(暂存离线消息)
type MessageBuffer struct {
mu sync.RWMutex
buffers map[string][]*types.UnifiedSynapseMessage // agent_id -> messages
config *BufferConfig
}
// BufferConfig 缓冲配置
type BufferConfig struct {
MaxPerAgent int // 每个 Agent 最大缓冲数
MaxAge time.Duration // 消息最大存活时间
CleanupInterval time.Duration // 清理间隔
}
// DefaultBufferConfig 返回默认缓冲配置
func DefaultBufferConfig() *BufferConfig {
return &BufferConfig{
MaxPerAgent: 1000,
MaxAge: 24 * time.Hour,
CleanupInterval: 1 * time.Hour,
}
}
// NewMessageBuffer 创建消息缓冲
func NewMessageBuffer(config *BufferConfig) *MessageBuffer {
if config == nil {
config = DefaultBufferConfig()
}
mb := &MessageBuffer{
buffers: make(map[string][]*types.UnifiedSynapseMessage),
config: config,
}
// 启动清理协程
go mb.cleanupLoop()
return mb
}
// BufferMessage 缓冲消息
func (mb *MessageBuffer) BufferMessage(agentID string, msg *types.UnifiedSynapseMessage) error {
mb.mu.Lock()
defer mb.mu.Unlock()
buf, ok := mb.buffers[agentID]
if !ok {
buf = make([]*types.UnifiedSynapseMessage, 0)
}
if len(buf) >= mb.config.MaxPerAgent {
// 丢弃最旧的
buf = buf[1:]
}
mb.buffers[agentID] = append(buf, msg)
log.Printf("[灵泊] 缓冲消息: %s -> %s (缓冲区大小: %d)",
msg.Sender.AgentID, agentID, len(mb.buffers[agentID]))
return nil
}
// FlushBuffer 清空缓冲并返回所有消息
func (mb *MessageBuffer) FlushBuffer(agentID string) []*types.UnifiedSynapseMessage {
mb.mu.Lock()
defer mb.mu.Unlock()
buf, ok := mb.buffers[agentID]
if !ok {
return []*types.UnifiedSynapseMessage{}
}
delete(mb.buffers, agentID)
log.Printf("[灵泊] 清空缓冲: %s (消息数: %d)", agentID, len(buf))
return buf
}
// GetBufferSize 获取缓冲大小
func (mb *MessageBuffer) GetBufferSize(agentID string) int {
mb.mu.RLock()
defer mb.mu.RUnlock()
return len(mb.buffers[agentID])
}
// cleanupLoop 清理过期消息
func (mb *MessageBuffer) cleanupLoop() {
ticker := time.NewTicker(mb.config.CleanupInterval)
defer ticker.Stop()
for range ticker.C {
mb.cleanup()
}
}
func (mb *MessageBuffer) cleanup() {
mb.mu.Lock()
defer mb.mu.Unlock()
now := time.Now()
cleaned := 0
for agentID, buf := range mb.buffers {
var newBuf []*types.UnifiedSynapseMessage
for _, msg := range buf {
msgTime, _ := time.Parse(time.RFC3339, string(msg.Timestamp))
if now.Sub(msgTime) < mb.config.MaxAge {
newBuf = append(newBuf, msg)
} else {
cleaned++
}
}
mb.buffers[agentID] = newBuf
}
if cleaned > 0 {
log.Printf("[灵泊] 清理过期消息: %d 条", cleaned)
}
}
// =============================================================================
// Retry Manager - 重试管理器
// =============================================================================
// RetryManager 重试管理器
type RetryManager struct {
mu sync.RWMutex
retries map[string]*RetryEntry
config *RetryConfig
}
// RetryEntry 重试条目
type RetryEntry struct {
ID string
Message *types.UnifiedSynapseMessage
Target string
RetryCount int
MaxRetries int
NextRetry time.Time
LastError string
BackoffBase time.Duration
}
// RetryConfig 重试配置
type RetryConfig struct {
MaxRetries int
BaseDelay time.Duration
MaxDelay time.Duration
JitterFactor float64
}
// DefaultRetryConfig 返回默认重试配置
func DefaultRetryConfig() *RetryConfig {
return &RetryConfig{
MaxRetries: 5,
BaseDelay: 1 * time.Second,
MaxDelay: 5 * time.Minute,
JitterFactor: 0.1,
}
}
// NewRetryManager 创建重试管理器
func NewRetryManager(config *RetryConfig) *RetryManager {
if config == nil {
config = DefaultRetryConfig()
}
return &RetryManager{
retries: make(map[string]*RetryEntry),
config: config,
}
}
// AddRetry 添加重试条目
func (rm *RetryManager) AddRetry(msg *types.UnifiedSynapseMessage, target string) {
rm.mu.Lock()
defer rm.mu.Unlock()
id := fmt.Sprintf("retry-%d", time.Now().UnixNano())
entry := &RetryEntry{
ID: id,
Message: msg,
Target: target,
RetryCount: 0,
MaxRetries: rm.config.MaxRetries,
NextRetry: time.Now().Add(rm.config.BaseDelay),
BackoffBase: rm.config.BaseDelay,
}
rm.retries[id] = entry
log.Printf("[灵泊] 添加重试: %s -> %s (最多 %d 次)", id, target, rm.config.MaxRetries)
}
// GetPendingRetries 获取待重试的条目
func (rm *RetryManager) GetPendingRetries() []*RetryEntry {
rm.mu.RLock()
defer rm.mu.RUnlock()
now := time.Now()
var pending []*RetryEntry
for _, entry := range rm.retries {
if now.After(entry.NextRetry) && entry.RetryCount < entry.MaxRetries {
pending = append(pending, entry)
}
}
return pending
}
// MarkSuccess 标记成功
func (rm *RetryManager) MarkSuccess(retryID string) {
rm.mu.Lock()
defer rm.mu.Unlock()
delete(rm.retries, retryID)
log.Printf("[灵泊] 重试成功: %s", retryID)
}
// MarkFailure 标记失败
func (rm *RetryManager) MarkFailure(retryID string, err error) bool {
rm.mu.Lock()
defer rm.mu.Unlock()
entry, ok := rm.retries[retryID]
if !ok {
return false
}
entry.RetryCount++
entry.LastError = err.Error()
if entry.RetryCount >= entry.MaxRetries {
delete(rm.retries, retryID)
log.Printf("[灵泊] 重试失败(已达上限): %s (重试 %d 次)", retryID, entry.RetryCount)
return false
}
// 指数退避 + 抖动
delay := entry.BackoffBase * time.Duration(1<<uint(entry.RetryCount-1))
if delay > rm.config.MaxDelay {
delay = rm.config.MaxDelay
}
entry.NextRetry = time.Now().Add(delay)
log.Printf("[灵泊] 重试失败: %s (%d/%d, 下次: %v)",
retryID, entry.RetryCount, entry.MaxRetries, delay)
return true
}
// Stats 获取统计信息
func (rm *RetryManager) Stats() (pending, failed int) {
rm.mu.RLock()
defer rm.mu.RUnlock()
for _, entry := range rm.retries {
if entry.RetryCount == 0 {
pending++
}
}
return pending, 0
}
// =============================================================================
// Harbor - 灵泊整体入口
// =============================================================================
// Harbor 灵泊 - 异步队列与暂存
type Harbor struct {
Queue *MessageQueue
Buffer *MessageBuffer
Retry *RetryManager
}
// NewHarbor 创建灵泊
func NewHarbor() *Harbor {
return &Harbor{
Queue: NewMessageQueue(nil),
Buffer: NewMessageBuffer(nil),
Retry: NewRetryManager(nil),
}
}
// SendWithRetry 发送消息并重试
func (h *Harbor) SendWithRetry(ctx context.Context, msg *types.UnifiedSynapseMessage, target string, sendFunc func(ctx context.Context, msg *types.UnifiedSynapseMessage, target string) error) error {
// 先入队
qm, err := h.Queue.Enqueue(msg, target, 5)
if err != nil {
return err
}
// 尝试发送
err = sendFunc(ctx, msg, target)
if err == nil {
h.Queue.MarkDelivered(qm.ID)
return nil
}
// 失败,加入重试
h.Retry.AddRetry(msg, target)
h.Queue.MarkFailed(qm.ID, err)
return err
}
// BufferForOffline 为离线 Agent 缓冲消息
func (h *Harbor) BufferForOffline(agentID string, msg *types.UnifiedSynapseMessage) {
h.Buffer.BufferMessage(agentID, msg)
}
// FlushAndSend 清空缓冲并发送
func (h *Harbor) FlushAndSend(agentID string, sendFunc func(msg *types.UnifiedSynapseMessage) error) int {
msgs := h.Buffer.FlushBuffer(agentID)
sent := 0
for _, msg := range msgs {
if err := sendFunc(msg); err == nil {
sent++
}
}
return sent
}
// ToJSON 导出为 JSON
func (qm *QueuedMessage) ToJSON() string {
data, _ := json.MarshalIndent(qm, "", " ")
return string(data)
}