3.8 KiB
3.8 KiB
CDR Redis Stream Runbook
任务:S22 - CDR Redis Stream
完成时间:2026-06-21 19:34 +08:00
1. 目标
S22 建立原始 CDR 的 Redis Stream 契约和可靠消费基础,不做计费、不扣余额、不写正式 rated CDR。计费与余额扣减留给 S23。
2. Redis Key
| Key | 用途 |
|---|---|
stream:cdr_payload |
OpenSIPS 写入的原始 CDR Stream |
stream:cdr_deadletter |
CDR Worker 校验或处理失败后的死信 Stream |
billing-workers |
stream:cdr_payload 的 Consumer Group |
lock:cdr:{event_id} |
Worker 处理幂等占位,避免同一 event_id 重复处理 |
3. 事件字段
S22 事件 schema version 为 1。必填字段:
schema_version
event_id
idempotency_key
call_id
node_id
opensips_instance
ingress_a_ip
rtpengine_node
source_ip
sip_code
hangup_reason
created_at
多 A 预留字段已经纳入契约:
node_id:A 节点 ID,当前单 A 为a1。opensips_instance:OpenSIPS 实例名,当前为opensips-a1。ingress_a_ip:接收 SIP 的 A 节点地址,当前为100.90.90.90。rtpengine_node:媒体节点,当前每台 A 绑定本机 RTPEngine,因此为a1。
S22 阶段 A 端尚未进入真实落地路由,因此失败 CDR 的供应商、录音、应答时间等字段使用 none 或 0 占位。S23/S24 后续在不破坏 schema 的前提下补充真实计费和录音字段。
4. Producer
Server A OpenSIPS 在 route[S20_CDR_XADD] 中执行:
XADD stream:cdr_payload * schema_version 1 ... created_at <unix>
当前 A 端字段已从 S20 冒烟格式升级为 S22 正式契约。A 端配置备份:
/var/backups/lisglosips-s22/20260621T113157Z/opensips.cfg
5. Consumer
packages/redis/src/cdr-stream.ts 提供:
ensureCdrConsumerGrouppublishCdrEventparseCdrStreamEventprocessCdrBatchprocessPendingCdrBatch
apps/worker-cdr 已从骨架改为 Redis Stream Worker:
- 读取
REDIS_URL。 - 确认
billing-workersConsumer Group。 - 先用
XAUTOCLAIM重领 Pending,再用XREADGROUP读取新消息。 - 用
lock:cdr:{event_id}做幂等占位。 - 处理成功或重复消息后
XACK。 - 校验失败或处理异常时写入
stream:cdr_deadletter并XACK原消息。
当前 handler 只记录并接受原始 CDR,不执行计费和余额扣减。
6. 验证结果
- 本地
packages/redis/src/cdr-stream.spec.ts6 条测试通过,覆盖发布、解析、Consumer Group 幂等创建、XREADGROUP/XACK、重复event_id、死信和XAUTOCLAIMPending 重领。 corepack pnpm@10.33.0 lint通过。corepack pnpm@10.33.0 typecheck通过。corepack pnpm@10.33.0 build通过。- A
opensips -C -f /etc/opensips/opensips.cfg通过,opensips.serviceactive。 - Redis
XGROUP CREATE stream:cdr_payload billing-workers 0 MKSTREAM返回OK。 - Redis
XINFO GROUPS stream:cdr_payload显示billing-workers,pending 为0。 - T 发起 INVITE 后,A 返回
SIP/2.0 503 Config Missing,Redis Stream 最新事件包含schema_version=1、idempotency_key、node_id=a1、opensips_instance=opensips-a1、ingress_a_ip=100.90.90.90、rtpengine_node=a1。
7. 回滚
代码回滚:
- 恢复
packages/redis/src/index.ts、packages/redis/src/cdr-stream.ts、packages/redis/src/cdr-stream.spec.ts、apps/worker-cdr/src/main.ts。 - 重新执行
corepack pnpm@10.33.0 build。
Server A 回滚:
sudo cp -a /var/backups/lisglosips-s22/20260621T113157Z/opensips.cfg /etc/opensips/opensips.cfg
sudo opensips -C -f /etc/opensips/opensips.cfg
sudo systemctl restart opensips
Redis Consumer Group 可保留,不影响 Producer;如必须移除,应先确认没有未处理 Pending,再使用受控 Redis 维护窗口删除 group。S22 不删除 Stream 数据。