166 lines
7.2 KiB
Markdown
166 lines
7.2 KiB
Markdown
# HY Voice Room
|
||
|
||
HY Voice Room 是语音房后端 monorepo。当前架构不再自研 IM 长连接服务:房间业务状态由 `room-service` 承担,客户端实时消息接入腾讯云 IM,服务端只负责签发 UserSig、创建房间群组、发送房间系统消息。
|
||
|
||
## Architecture
|
||
|
||
核心边界:
|
||
|
||
任何开发,不需要历史兼容,目前只是在开发阶段
|
||
|
||
- `gateway-service` 是客户端 HTTP JSON 入口,负责鉴权、协议转换、签发腾讯云 IM UserSig,以及调用内部 gRPC 服务。
|
||
- `room-service` 是房间业务状态 owner,负责 Room Cell、麦位、房间 presence、本地礼物榜、送礼落房间、snapshot、command log、outbox、Redis lease,以及腾讯云 IM 群组/系统消息桥接。
|
||
- `wallet-service` 当前提供 `DebitGift` 送礼扣费语义,余额、流水和幂等记录落 MySQL;`room-service` 的 `SendGift` 同步调用它。
|
||
- `user-service` 当前承接登录、三方注册、密码、refresh session、用户主数据、默认短号和临时靓号基础能力。
|
||
- `activity-service` 当前提供活动 gRPC 查询骨架和房间事件 consumer 边界,尚未接入 `room_outbox` 消费主循环。
|
||
- 腾讯云 IM 承担客户端长连接、群消息、公屏、单聊、离线/漫游和全球接入能力;本仓库不再部署 `im-service`。
|
||
|
||
协议边界:
|
||
|
||
- 客户端命令面:`gateway-service` 暴露 HTTP JSON。
|
||
- 客户端实时面:腾讯云 IM SDK,客户端用 gateway 签发的 UserSig 登录。
|
||
- 服务内部调用:gRPC + protobuf,契约在 `api/proto`。
|
||
- 房间外事件:protobuf outbox,当前先落 MySQL。
|
||
|
||
## Ports
|
||
|
||
本地和 Docker 编排统一使用 `13xxx` 端口:
|
||
|
||
| Component | Port | Purpose |
|
||
| ------------------ | --------------: | ----------------- |
|
||
| `gateway-service` | `13000` | HTTP JSON |
|
||
| `room-service` | `13001` | gRPC |
|
||
| `wallet-service` | `13004` | gRPC |
|
||
| `user-service` | `13005` | gRPC |
|
||
| `activity-service` | `13006` | gRPC |
|
||
| MySQL | `23306 -> 3306` | local Docker only |
|
||
| Redis | `13379 -> 6379` | local Docker only |
|
||
|
||
## Run Locally
|
||
|
||
Use Docker for local and test environments:
|
||
|
||
```bash
|
||
make up
|
||
```
|
||
|
||
Service aliases: `gs/gateway`, `rs/room`, `ws/wallet`, `us/user`, `as/activity`, `mysql`, `redis`.
|
||
|
||
Common commands:
|
||
|
||
```bash
|
||
make test
|
||
make build
|
||
make addr
|
||
make logs
|
||
make rebuild rs
|
||
make down
|
||
```
|
||
|
||
For direct host execution, each service reads its own `services/<service>/configs/config.yaml`; `room-service`、`user-service`、`wallet-service` 和 `activity-service` 都要求 MySQL 可用。
|
||
|
||
## Tencent IM
|
||
|
||
客户端获取腾讯云 IM 登录票据:
|
||
|
||
```text
|
||
GET /api/v1/im/usersig
|
||
Authorization: Bearer <access_token>
|
||
```
|
||
|
||
响应 `data` 包含:
|
||
|
||
```json
|
||
{
|
||
"sdk_app_id": 1400000000,
|
||
"user_id": "10001",
|
||
"user_sig": "xxx",
|
||
"expire_at_ms": 1710000000000
|
||
}
|
||
```
|
||
|
||
房间流程:
|
||
|
||
- 客户端先经 gateway 调 `JoinRoom`,让用户进入 `room-service` presence。
|
||
- 客户端再用腾讯云 IM SDK 登录并加入 `room_id` 对应的群组。
|
||
- `gateway-service` 创建房间时从 access token 取当前 `user_id` 写入 `RequestMeta.actor_user_id`;`room-service` 由该 actor 收敛 owner 和默认 host,外部 CreateRoom 请求体不接收 `owner_user_id` 和 `host_user_id`。
|
||
- 房间 HTTP 命令支持请求体 `command_id` 作为幂等键;`request_id` 只做链路追踪。
|
||
- `room-service` 在 `CreateRoom` 时通过腾讯云 IM REST API 创建同名群组。
|
||
- `room-service` 在上下麦、禁言、踢人、送礼等命令提交后,通过腾讯云 IM REST API 发送房间系统自定义消息。
|
||
- `gateway-service` 已提供 `/api/v1/tencent-im/callback`,支持腾讯云 IM 入群前和发言前回调,并分别回查 `VerifyRoomPresence` 和 `CheckSpeakPermission`。
|
||
|
||
腾讯云 IM 配置分别在:
|
||
|
||
```text
|
||
services/gateway-service/configs/config.yaml
|
||
services/room-service/configs/config.yaml
|
||
services/room-service/configs/config.tencent.example.yaml
|
||
```
|
||
|
||
本地联调腾讯云 IM 时,`gateway-service` 和 `room-service` 必须使用同一组 `sdk_app_id`、`secret_key`,`room-service` 还必须配置匹配的 `admin_identifier` 和区域 `endpoint`。
|
||
|
||
基础闭环实现顺序见:
|
||
|
||
```text
|
||
docs/voice-room-basic-loop-implementation.md
|
||
```
|
||
|
||
## Storage Model
|
||
|
||
MySQL 是本地和线上运行的持久化底座:
|
||
|
||
- `room-service`: `hyapp_room`,保存 room meta、snapshot、command log 和 outbox。
|
||
- `user-service`: `hyapp_user`,保存用户主数据、注册资料快照、登录身份、session、默认短号和靓号租约。
|
||
- `wallet-service`: `hyapp_wallet`,保存余额、账务流水和扣费幂等记录。
|
||
- `activity-service`: `hyapp_activity`,保存活动状态、事件消费幂等和 activity outbox。
|
||
|
||
`room-service` uses MySQL as the room recovery durable source:
|
||
|
||
- `rooms`: room metadata.
|
||
- `room_snapshots`: latest room state snapshot.
|
||
- `room_command_log`: replayable command log for recovery.
|
||
- `room_outbox`: async event records.
|
||
|
||
`room-service` uses Redis for current room ownership:
|
||
|
||
```text
|
||
room:route:{room_id}
|
||
```
|
||
|
||
Redis lease 只保存当前执行权,不能作为恢复来源。恢复来源是 MySQL snapshot + command log。
|
||
|
||
## Room Recovery
|
||
|
||
A room is not permanently bound to one process. A node is only the current executor while its Redis lease is valid.
|
||
|
||
Recovery path:
|
||
|
||
1. A request reaches a node that can own the room.
|
||
2. The node acquires or renews `room:route:{room_id}`.
|
||
3. If the local Room Cell does not exist, it loads the latest MySQL snapshot.
|
||
4. It replays command log records after the snapshot version.
|
||
5. It installs a local Room Cell and continues processing.
|
||
|
||
This is single-active recovery, not hot standby. Short retry/reconnect windows are expected during node failure.
|
||
|
||
## Project Layout
|
||
|
||
```text
|
||
api/proto/ protobuf contracts
|
||
pkg/ shared packages, including Tencent IM signing/client
|
||
services/gateway-service/ HTTP entry service
|
||
services/room-service/ Room Cell and durable room state
|
||
services/wallet-service/ wallet DebitGift and ledger foundation
|
||
services/user-service/ user service
|
||
services/activity-service/ activity foundation and room event consumer boundary
|
||
```
|
||
|
||
## Current Limits
|
||
|
||
- 腾讯云 IM 回调入口已经落在 gateway;线上仍必须在腾讯云 IM 控制台启用 `Group.CallbackBeforeApplyJoinGroup` 和 `Group.CallbackBeforeSendMsg`,并配置公网 callback URL 与鉴权 token。
|
||
- 房间命令未传 `command_id` 时,gateway 会自动生成新值;客户端 HTTP 重试同一业务动作时必须复用原 `command_id` 才能命中 room-service command log 幂等。
|
||
- `wallet-service` App 运行路径使用 MySQL 扣费事务;本地需要先通过 compose initdb 建好 `hyapp_wallet`。
|
||
- `activity-service` 当前只有同步查询和 consumer 边界;`room_outbox` 到 activity/audit 的实际消费链路未完整实现。
|
||
- `room-service` App 已启动 `room_outbox` 补偿 worker;该状态只表示腾讯云 IM 系统消息补偿结果,activity/audit 后续必须使用独立消费位点。
|
||
- JWT、腾讯云 IM secret 等本地配置只能用开发值,线上必须走安全配置源。
|