第一位参与者工作正常,第二位参与者破坏了在AWS上的媒体发布功能

前端开发 2026-07-08

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生产环境中失败。

本地主机行为

  1. 用户A 进入房间。
  2. 用户B 进入同一个房间。
  3. 两位用户都能看到并听见对方。
  4. 摄像头和麦克风保持稳定。

AWS行为

  1. 用户A 进入房间。
  2. 摄像头和麦克风初始时工作正常。
  3. 用户B 进入同一个房间。
  4. 用户A 的摄像头/音频丢失。
  5. 用户A 只显示头像首字母,替代视频流。
  6. 点对点通信停止工作。

控制台显示:

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(最新)
  • 在普通窗口和私人浏览窗口中均有复现
  • 已授权摄像头和麦克风权限

在线站点:https://sangama.ddns.net

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

仅在生产环境出现,而本地主机工作正常?

这看起来更像以下哪种情形:

  1. 房间生命周期清理问题?
  2. 导致重新连接/重渲染的React effect?
  3. connect() 与轨道发布之间的竞态条件?
  4. WebRTC协商问题?
  5. LiveKit特定的生产部署问题?

对于下一步该从哪里排查,任何指引都将不胜感激。

解决方案

我之前也遇到过非常类似的问题,在我的情况中并不是LiveKit Cloud的问题。

错误:

PublishTrackError: publishing rejected as engine not connected within timeout

随后是:

could not createOffer with closed peer connection

通常意味着在LiveKit尝试发布或重新协商轨道时,WebRTC对等连接已经被关闭。

查看代码时,我会检查创建 RoomuseEffect 是否被触发多次。该effect的依赖为:

[serverUrl, token, notifyLeaveComplete]

如果在渲染之间 notifyLeaveComplete 发生变化,React将执行清理、调用 room.disconnect(),并创建一个新的房间实例。这可能会让LiveKit尝试在已经关闭的连接上进行发布。

我会在房间创建与清理处添加一些日志,看看在第二个参与者加入时房间是否会被重新创建。

我还会检查Firefox的 about:webrtc 页,以及浏览器控制台中的ICE失败情况。如果信令连接保持连接,但ICE进入 faileddisconnected,那么很可能是WebRTC/网络问题,而非应用问题。

既然本地主机使用相同的LiveKit项目和令牌也能工作,我猜测可能是以下之一:

  1. 引发 room.disconnect() 的React生命周期/重新挂载问题。
  2. 生产环境中的ICE/WebRTC连接问题。
  3. 连接重新建立时正在发布轨道造成的竞态条件。

我会先确认在整个会议中只有一个 Room 实例存在,并且在参与者加入时该fetch/effect不会再次运行。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章