feat: 实现消息分发推送确认撤回与启动恢复

This commit is contained in:
Nixevol
2026-09-30 07:32:53 +08:00
parent 34e7c2827f
commit bdd1d9e9f4
12 changed files with 2395 additions and 119 deletions
+61 -50
View File
@@ -1,7 +1,7 @@
package message
import (
"context"
"sync"
"time"
"git.asio.asia/nixevol/NixMsg/internal/app/port"
@@ -18,11 +18,6 @@ const (
StateCompleted = "completed"
)
// 投递状态。
const (
DeliveryPending = "pending"
)
// talk_grants.kind。
const (
GrantKindPassword = "password"
@@ -32,7 +27,7 @@ const (
// 请求频率桶默认突发容量(DEVELOPMENT 6.10;配置无单独字段)。
const defaultRequestBurst = 100
// Limits 是提交所需的配置上限(来自 config.LimitsConfig)。
// Limits 是消息子系统所需配置上限。
type Limits struct {
MaxBodyBytes int
MaxMetaBytes int
@@ -44,11 +39,16 @@ type Limits struct {
MaxPendingPerSender int
MaxPendingPerReceiver int
GraceSeconds int64
AckTimeoutSeconds int64
DeliveryWindow int
ReceiptWindow int
RecordRetentionDays int
ReceiptRetentionDays int
IdempotencyHours int
}
// LimitsFromConfig 从平台配置构造 Limits。
func LimitsFromConfig(c config.LimitsConfig) Limits {
burst := defaultRequestBurst
return Limits{
MaxBodyBytes: c.MaxBodyBytes,
MaxMetaBytes: c.MaxMetaBytes,
@@ -56,14 +56,26 @@ func LimitsFromConfig(c config.LimitsConfig) Limits {
MaxTTLSeconds: int64(c.MaxTTLSeconds),
MaxScheduleSeconds: int64(c.MaxScheduleSeconds),
RequestsPerSecond: float64(c.RequestsPerSecond),
RequestBurst: burst,
RequestBurst: defaultRequestBurst,
MaxPendingPerSender: c.MaxPendingPerSender,
MaxPendingPerReceiver: c.MaxPendingPerReceiver,
GraceSeconds: int64(c.GraceSeconds),
AckTimeoutSeconds: int64(c.AckTimeoutSeconds),
DeliveryWindow: c.DeliveryWindow,
ReceiptWindow: c.ReceiptWindow,
}
}
// App 实现 Service 的提交路径(M1);其余方法暂返回未实现或空操作。
// LimitsFromFullConfig 附带保留天数等顶层配置。
func LimitsFromFullConfig(cfg config.Config) Limits {
lim := LimitsFromConfig(cfg.Limits)
lim.RecordRetentionDays = cfg.RecordRetentionDays
lim.ReceiptRetentionDays = cfg.ReceiptRetentionDays
lim.IdempotencyHours = cfg.IdempotencyHours
return lim
}
// App 实现 Service:提交、分发、推送、确认、撤回、回执、清理与启动恢复。
type App struct {
db *store.DB
lim Limits
@@ -71,6 +83,15 @@ type App struct {
locks auth.LoginLocks
nowFn func() time.Time
rates *rateLimiter
down port.Downlink
conns ConnRegistry
mu sync.Mutex
largeSem chan struct{}
largeHeld map[string]bool
pendingRevoke []revokeJob
repushTimers map[string]*time.Timer
}
// Option 配置 App。
@@ -86,17 +107,41 @@ func WithLocks(locks auth.LoginLocks) Option {
return func(a *App) { a.locks = locks }
}
// New 创建消息服务实现。hash 用于校验对话密码;locks 可为 nil。
// WithDownlink 注入下行发布器(未接线时测试用 RecordingDownlink)。
func WithDownlink(d port.Downlink) Option {
return func(a *App) { a.down = d }
}
// WithConnRegistry 注入连接查询。
func WithConnRegistry(c ConnRegistry) Option {
return func(a *App) { a.conns = c }
}
// New 创建消息服务实现。
func New(db *store.DB, lim Limits, hash auth.HashPool, opts ...Option) *App {
if lim.RequestBurst <= 0 {
lim.RequestBurst = defaultRequestBurst
}
if lim.DeliveryWindow <= 0 {
lim.DeliveryWindow = defaultDeliveryWindow
}
if lim.ReceiptWindow <= 0 {
lim.ReceiptWindow = defaultReceiptWindow
}
if lim.AckTimeoutSeconds <= 0 {
lim.AckTimeoutSeconds = 300
}
if lim.GraceSeconds < 0 {
lim.GraceSeconds = 60
}
a := &App{
db: db,
lim: lim,
hash: hash,
nowFn: time.Now,
rates: newRateLimiter(lim.RequestsPerSecond, lim.RequestBurst),
db: db,
lim: lim,
hash: hash,
nowFn: time.Now,
rates: newRateLimiter(lim.RequestsPerSecond, lim.RequestBurst),
largeSem: make(chan struct{}, maxLargeInflight),
largeHeld: make(map[string]bool),
}
for _, opt := range opts {
opt(a)
@@ -116,38 +161,4 @@ func (a *App) protocolLimits() protocol.Limits {
}
}
func (a *App) Ack(context.Context, string, *protocol.Ack) (AckResult, error) {
return AckResult{}, ErrNotImplemented
}
func (a *App) Recall(context.Context, string, *protocol.Recall) (protocol.RecallData, error) {
return protocol.RecallData{}, ErrNotImplemented
}
func (a *App) Status(context.Context, string, *protocol.Status) (any, error) {
return nil, ErrNotImplemented
}
func (a *App) ReceiptAck(context.Context, string, *protocol.ReceiptAck) error {
return ErrNotImplemented
}
func (a *App) DispatchDue(context.Context, int64, int) (int, error) {
return 0, nil
}
func (a *App) PushPending(context.Context, string, port.ConnID) error {
return nil
}
func (a *App) OnPublishDropped(context.Context, string, port.ConnID, []byte) error {
return nil
}
func (a *App) CleanupOnce(context.Context, int64) error { return nil }
func (a *App) RecoverOnStart(context.Context) error { return nil }
func (a *App) WakePush(string) {}
var _ Service = (*App)(nil)