hyapp-server/README.md
2026-04-25 13:21:39 +08:00

6.0 KiB
Raw Blame History

HY Voice Room

HY Voice Room 是一个语音房后端 monorepo。当前重点不是做通用 IM 平台而是把语音房的实时业务闭环跑起来HTTP 命令入口、房间 Room Cell、WebSocket 消息投递、MySQL 状态恢复、Redis 房间路由。

Architecture

核心边界:

  • gateway-service 是客户端 HTTP JSON 入口,负责鉴权、协议转换和调用 room-service。它不持有房间状态,也不终止 WebSocket。
  • room-service 是房间业务状态 owner负责 Room Cell、麦位、房间 presence、本地礼物榜、送礼落房间、snapshot、command log、outbox、Redis lease 路由。
  • im-service 是连接和消息 owner负责 WebSocket、房间频道订阅、公屏、房间系统消息、单聊、ACK、最近房间历史。
  • wallet-service 当前提供送礼扣费 stubroom-serviceSendGift 同步调用它。
  • user-serviceactivity-service 当前是独立进程骨架,后续分别承接用户主数据和活动消费。

外部协议和内部协议:

  • 客户端命令面:gateway-service 暴露 HTTP JSON。
  • 客户端实时面:im-service 暴露 WebSocket JSON。
  • 服务内部调用gRPC + protobuf契约在 api/proto
  • 房间外事件protobuf outbox当前先落 MySQL。

Ports

本地和 Docker 编排统一使用 13xxx 端口:

Component Port Purpose
gateway-service 13000 HTTP JSON
room-service 13001 gRPC
im-service 13002 HTTP/WebSocket
im-service 13003 gRPC
wallet-service 13004 gRPC
user-service 13005 gRPC
activity-service 13006 gRPC
MySQL 13306 -> 3306 local Docker only
Redis 13379 -> 6379 local Docker only

Run Locally

Use Docker for local and test environments:

make up

make up starts the stack in detached mode and prints host-local, host-LAN, and Docker-network addresses for every service port.

Follow logs:

make logs

Print addresses again without restarting services:

make addr

Operate one service:

make up rs
make rebuild rs
make restart rs
make stop rs
make logs rs
make ps rs

Service aliases: gs/gateway, rs/room, is/im, ws/wallet, us/user, as/activity, mysql, redis.

Stop services:

make down

The compose stack starts MySQL and Redis. MySQL schema is initialized from:

services/room-service/deploy/mysql/initdb/001_room_service.sql

For direct host execution, each service reads its own services/<service>/configs/config.yaml.

Development Commands

make proto
make test
make build
make up
make addr
make logs
make ps
make up rs
make rebuild rs
make restart rs
make stop rs
make logs rs
make ps rs
make down

make proto requires protoc, protoc-gen-go, and protoc-gen-go-grpc.

Storage Model

room-service uses MySQL as the 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:

room:route:{room_id}

The Redis value stores room_id, node_id, lease_token, and expires_at_ms. Lease acquisition is atomic through Lua in internal/router/redis_directory.go.

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.

Cloud Deployment

Local Docker uses:

services/room-service/configs/config.docker.yaml

Tencent Cloud deployment should follow:

services/room-service/configs/config.tencent.example.yaml

Set the real Tencent Cloud MySQL DSN, Redis address, Redis password, and internal service DNS names. Keep mysql_auto_migrate: false online; schema changes should run through a controlled migration process.

Deployment lifecycle requirements are documented in:

docs/health-checks.md
docs/graceful-shutdown.md

API Entry Points

Gateway HTTP routes:

  • POST /api/v1/rooms/create
  • POST /api/v1/rooms/join
  • POST /api/v1/rooms/leave
  • POST /api/v1/rooms/mic/up
  • POST /api/v1/rooms/mic/down
  • POST /api/v1/rooms/mic/change
  • POST /api/v1/rooms/user/mute
  • POST /api/v1/rooms/user/kick
  • POST /api/v1/rooms/gift/send

Gateway business responses use one envelope:

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_xxx",
  "data": {}
}

request_id is generated by gateway unless the client sends X-Request-ID. The same value is propagated to internal gRPC RequestMeta.request_id.

IM WebSocket path:

ws://127.0.0.1:13002/ws

The first WS frame must be auth; later frames include subscribe_room, unsubscribe_room, room_chat_send, peer_send, ack, and pull_history.

Project Layout

api/proto/                 protobuf contracts
pkg/                       small shared packages
services/gateway-service/  HTTP entry service
services/room-service/     Room Cell and durable room state
services/im-service/       WebSocket and message delivery
services/wallet-service/   wallet stub
services/user-service/     user stub
services/activity-service/ activity stub

Current Limits

  • wallet-service, user-service, and activity-service are still stubs.
  • room-service outbox is durable, but external activity consumption is not implemented yet.
  • im-service keeps message/session state in memory; scaling it across nodes still needs shared online routing or a delivery bus.
  • JWT secrets in local config are development values only.