目录与心跳任务的 Map 节流
背景
国标设备上线后需要 周期性 Catalog,同时要对 注册/心跳超时 做检测。若「每个设备单独起 time.Ticker」,上万路设备会产生 数万 goroutine + 数万定时器,调度与内存开销不可接受。本仓库用 共享 Map + 全局1 秒刻度 统一驱动。
项目中的做法
1. Catalog:SipCatalogLoop + SipCatalogLoopMap
-设备状态变更时向 SipCatalogLoop channel 投递 SipCatalogLoopReq(上线 Online=true 写入 Map,下线移除)。
CatalogLoopLogic.proc中 每秒遍历SipCatalogLoopMap.Values()。- 通过
item.Now % val.Unix() != Config.Sip.CatalogInterval过滤,只在 与配置间隔对齐的秒 向SipSendCatalog投递真正发送任务。
效果:一个定时器服务所有设备,Catalog 发送节奏由配置 CatalogInterval 与当前 Unix 秒对齐控制。
flowchart TB
Reg["REGISTER/状态变更"]
Ch["SipCatalogLoop"]
Map["SipCatalogLoopMap\nkey =设备/请求 ID"]
Tick["Ticker 1s"]
Send["SipSendCatalog"]
Reg --> Ch --> Map
Tick --> Scan["遍历 Map"]
Map --> Scan
Scan -->|"满足 CatalogInterval 对齐"| Send
2. 心跳:SipHeartbeatLoop + SipHeartbeatLoopMap
- 注册/心跳报文侧把任务写入
SipHeartbeatLoopMap。 HeartbeatOfflineLogic.proc每秒扫描条目:若 注册过期超过 10s 或 距上次心跳超过HeartbeatTimeout,从 Map 删除并投递SetDeviceOnline下线。
stateDiagram-v2
[*] --> InMap: 注册/心跳更新
InMap --> InMap: 每秒检查
InMap --> Offline: 超时 Offline --> [*] SetDeviceOnline false
要点
- 时间对齐:Catalog 使用取模对齐,设备多时会在同一秒「齐发」一批 Catalog,可能对 SIP 栈与设备侧造成尖峰;若现场出现设备侧处理不过来,可在 Map 扫描内加 随机抖动 或 分批 sleep(需改代码)。
- Map 与 channel 分工:channel 只做 增删事件,周期逻辑只在 单协程 里读 Map,避免多写者竞态(
xmap并发安全)。 - 可观测性:SSE
sev_state会输出SipCatalogLoopMap.Len()、SipHeartbeatLoopMap.Len(),用于判断泄漏或规模。
相关代码路径
core/app/sev/vss/internal/logic/gbs_proc/catalog_loop.gocore/app/sev/vss/internal/logic/gbs_proc/heartbeat_offline_loop.gocore/app/sev/vss/internal/types/types.go