第一位参与者工作正常,第二位参与者破坏了在AWS上的媒体发布功能
LiveKit在本地可用,但在AWS Docker部署中因 PublishTrackError: publishing rejected as engine not connected within timeout 而失败
我正在构建一个视频会议应用,使用:
- React 18
- Node.js / Express
- LiveKit Cloud
- Docker Compose
- AWS EC2
- Nginx反向代理
问题
应用在本地主机上工作正常,但在AWS生产环境中失败。
本地主机行为
- 用户A 进入房间。
- 用户B 进入同一个房间。
- 两位用户都能看到并听见对方。
- 摄像头和麦克风保持稳定。
AWS行为
- 用户A 进入房间。
- 摄像头和麦克风初始时工作正常。
- 用户B 进入同一个房间。
- 用户A 的摄像头/音频丢失。
- 用户A 只显示头像首字母,替代视频流。
- 点对点通信停止工作。
控制台显示:
PublishTrackError:
publishing rejected as engine not connected within timeout
随后是:
could not createOffer with closed peer connection
用户B 通常仍然保持连接,而用户A 会丢失媒体。
我已验证的内容
- 本地主机和AWS上的提交版本相同。
- 相同的源代码。
- 相同的LiveKit Cloud项目。
- Docker容器从头重新构建。
- Nginx配置修正并验证。
- 生产环境提供的是最新的React bundle。
- 令牌生成成功。
- 两位用户连接到同一个房间。
- 两位用户都收到有效的参与者ID。
示例日志:
connected to LiveKit Server
room: "kdx-irb-iwg"
participant: "Nithi"
connection state changed:
connecting -> connected
然后:
PublishTrackError:
publishing rejected as engine not connected within timeout
以及:
could not createOffer with closed peer connection
LiveKit令牌生成
const token = new AccessToken(
process.env.LIVEKIT_API_KEY,
process.env.LIVEKIT_API_SECRET,
{
identity: username,
}
);
token.addGrant({
roomJoin: true,
room: roomName,
canPublish: true,
canSubscribe: true,
});
房间生命周期代码
useEffect(() => {
if (!token || !serverUrl) return;
const r = new Room({ disconnectOnPageLeave: false });
setRoom(r);
let cancelled = false;
const start = async () => {
try {
await r.connect(serverUrl, token, {
autoSubscribe: true,
});
if (cancelled) {
await r.disconnect();
return;
}
await Promise.all([
r.localParticipant.setMicrophoneEnabled(true),
r.localParticipant.setCameraEnabled(true),
]);
} catch (err) {
console.error(
"[LiveKit] Failed to connect or publish tracks:",
err
);
}
};
start();
return () => {
cancelled = true;
r.disconnect();
};
}, [serverUrl, token, notifyLeaveComplete]);
媒体控制
onClick={() =>
localParticipant.setMicrophoneEnabled(
!isMicrophoneEnabled
)
}
onClick={() =>
localParticipant.setCameraEnabled(
!isCameraEnabled
)
}
环境
前端:
- React 18
- React Router
- LiveKit React组件
后端:
- Node.js
- Express
部署:
- Docker Compose
- AWS EC2 Ubuntu
- Nginx反向代理
- LiveKit Cloud
浏览器:
- Firefox(最新)
- 在普通窗口和私人浏览窗口中均有复现
- 已授权摄像头和麦克风权限
GitHub仓库:https://github.com/NiteeshGowda72/sangama-video-conferencing-platform
相关文件:
- frontend/src/pages/LiveKitMeeting.jsx
- frontend/src/pages/MeetingRoom.jsx
- backend/src/controllers/livekitController.js
- docker-compose.yml
问题
有没有人在生产环境看到过LiveKit抛出:
PublishTrackError:
publishing rejected as engine not connected within timeout
随后是:
could not createOffer with closed peer connection
仅在生产环境出现,而本地主机工作正常?
这看起来更像以下哪种情形:
- 房间生命周期清理问题?
- 导致重新连接/重渲染的React effect?
connect()与轨道发布之间的竞态条件?- WebRTC协商问题?
- LiveKit特定的生产部署问题?
对于下一步该从哪里排查,任何指引都将不胜感激。
解决方案
我之前也遇到过非常类似的问题,在我的情况中并不是LiveKit Cloud的问题。
错误:
PublishTrackError: publishing rejected as engine not connected within timeout
随后是:
could not createOffer with closed peer connection
通常意味着在LiveKit尝试发布或重新协商轨道时,WebRTC对等连接已经被关闭。
查看代码时,我会检查创建 Room 的 useEffect 是否被触发多次。该effect的依赖为:
[serverUrl, token, notifyLeaveComplete]
如果在渲染之间 notifyLeaveComplete 发生变化,React将执行清理、调用 room.disconnect(),并创建一个新的房间实例。这可能会让LiveKit尝试在已经关闭的连接上进行发布。
我会在房间创建与清理处添加一些日志,看看在第二个参与者加入时房间是否会被重新创建。
我还会检查Firefox的 about:webrtc 页,以及浏览器控制台中的ICE失败情况。如果信令连接保持连接,但ICE进入 failed 或 disconnected,那么很可能是WebRTC/网络问题,而非应用问题。
既然本地主机使用相同的LiveKit项目和令牌也能工作,我猜测可能是以下之一:
- 引发
room.disconnect()的React生命周期/重新挂载问题。 - 生产环境中的ICE/WebRTC连接问题。
- 连接重新建立时正在发布轨道造成的竞态条件。
我会先确认在整个会议中只有一个 Room 实例存在,并且在参与者加入时该fetch/effect不会再次运行。