Files
lisglosips/docs/CDR_REDIS_STREAM_RUNBOOK.md

3.8 KiB
Raw Permalink Blame History

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_idA 节点 ID,当前单 A 为 a1
  • opensips_instanceOpenSIPS 实例名,当前为 opensips-a1
  • ingress_a_ip:接收 SIP 的 A 节点地址,当前为 100.90.90.90
  • rtpengine_node:媒体节点,当前每台 A 绑定本机 RTPEngine,因此为 a1

S22 阶段 A 端尚未进入真实落地路由,因此失败 CDR 的供应商、录音、应答时间等字段使用 none0 占位。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 提供:

  • ensureCdrConsumerGroup
  • publishCdrEvent
  • parseCdrStreamEvent
  • processCdrBatch
  • processPendingCdrBatch

apps/worker-cdr 已从骨架改为 Redis Stream Worker

  1. 读取 REDIS_URL
  2. 确认 billing-workers Consumer Group。
  3. 先用 XAUTOCLAIM 重领 Pending,再用 XREADGROUP 读取新消息。
  4. lock:cdr:{event_id} 做幂等占位。
  5. 处理成功或重复消息后 XACK
  6. 校验失败或处理异常时写入 stream:cdr_deadletterXACK 原消息。

当前 handler 只记录并接受原始 CDR,不执行计费和余额扣减。

6. 验证结果

  • 本地 packages/redis/src/cdr-stream.spec.ts 6 条测试通过,覆盖发布、解析、Consumer Group 幂等创建、XREADGROUP/XACK、重复 event_id、死信和 XAUTOCLAIM Pending 重领。
  • 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.service active。
  • Redis XGROUP CREATE stream:cdr_payload billing-workers 0 MKSTREAM 返回 OK
  • Redis XINFO GROUPS stream:cdr_payload 显示 billing-workerspending 为 0
  • T 发起 INVITE 后,A 返回 SIP/2.0 503 Config MissingRedis Stream 最新事件包含 schema_version=1idempotency_keynode_id=a1opensips_instance=opensips-a1ingress_a_ip=100.90.90.90rtpengine_node=a1

7. 回滚

代码回滚:

  • 恢复 packages/redis/src/index.tspackages/redis/src/cdr-stream.tspackages/redis/src/cdr-stream.spec.tsapps/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 数据。