Initial LisgloSIPS V2 implementation

This commit is contained in:
hectorzhao
2026-06-22 10:56:38 +08:00
commit 5fa1bd35e9
303 changed files with 35644 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
# 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`。必填字段:
```text
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]` 中执行:
```text
XADD stream:cdr_payload * schema_version 1 ... created_at <unix>
```
当前 A 端字段已从 S20 冒烟格式升级为 S22 正式契约。A 端配置备份:
```text
/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_deadletter``XACK` 原消息。
当前 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-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 回滚:
```bash
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 数据。