-
Notifications
You must be signed in to change notification settings - Fork 41
Open
Description
问题描述
按照 WebRTC Demo 文档 在 macOS (M4, 24GB) 上完成本地部署后,前端页面显示:
Server at capacity. Please try again later.
点击 Start Call 无法发起通话。
根因分析
排查后端日志发现:
voice_chat - ERROR - routes.py:79:login - 登录失败: 503: 没有可用的推理服务
原因是 cpp_server/minicpmo_cpp_http_server.py 中的 register_service_node() 函数 只在服务启动时注册一次(第 821-823 行),没有周期性心跳保活机制:
# lifespan 函数中,只注册一次
try:
register_service_node(port=app.state.port, duplex_mode=app.state.default_duplex_mode)
except Exception as e:
print(f"服务节点注册失败: {e}", flush=True)而后端的 heartbeat_monitor 每隔几秒会检查并清理过期的服务注册。由于推理服务没有定期发送心跳,注册很快就被标记为过期并清除,导致后端找不到可用的推理节点。
此外,如果 deploy_all.sh 执行过程中推理服务因依赖缺失(如 ModuleNotFoundError: No module named 'librosa')等原因启动失败,手动修复后重启推理服务,也会因为心跳缺失而无法被后端识别。
复现步骤
- 按文档执行
deploy_all.sh --duplex - 等待所有 Docker 容器启动(前端/后端/LiveKit/Redis)
- 推理服务启动并成功注册
- 等待约 30-60 秒
- 打开
http://localhost:3000,点击 Start Call - 看到 "Server at capacity. Please try again later."
临时解决方案
在宿主机上运行一个定时重新注册的脚本:
LOCAL_IP=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | head -1 | awk '{print $2}')
while true; do
curl -s -X POST "http://localhost:8021/api/inference/register" \
-H "Content-Type: application/json" \
-d "{\"ip\": \"$LOCAL_IP\", \"port\": 9060, \"model_port\": 9060, \"model_type\": \"duplex\", \"session_type\": \"release\", \"service_name\": \"o45-cpp\"}"
sleep 30
done建议修复方案
在 minicpmo_cpp_http_server.py 中增加后台心跳线程,定期向后端重新注册:
import threading
def _heartbeat_loop(port: int, duplex_mode: bool, interval: int = 30):
"""后台心跳线程,定期向后端重新注册服务"""
while True:
try:
register_service_node(port=port, duplex_mode=duplex_mode)
except Exception as e:
print(f"心跳注册异常: {e}", flush=True)
time.sleep(interval)
# 在 lifespan 中启动时调用:
heartbeat_thread = threading.Thread(
target=_heartbeat_loop,
args=(app.state.port, app.state.default_duplex_mode),
daemon=True
)
heartbeat_thread.start()环境信息
- 设备: Mac mini M4, 24GB
- macOS: Darwin 25.2.0
- Docker Desktop: 4.59.1
- Python: 3.14.2
- 模型: MiniCPM-o-4_5 Q4_K_M
- 部署模式: duplex
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels