场景教程:直播房与聊天室

直播场景通常由两条链路组成:音视频流(外部 RTC/CDN)与互动消息(本项目聊天室)。本教程用聊天室承载弹幕、公告、禁言与在线人数,用房间级 KV 承载公告/榜单,用 RTC 绑定字段记录音视频房间号,并给出一套可运营的上线清单。

能力本项目的实现边界
弹幕 / 互动消息聊天室消息(仅在线广播、无历史、无 ACK)需要回看的弹幕请另存业务侧或使用普通群
公告 / 置顶房间级 KV(chatroom.kv.set,可带 TTL)随房间销毁;客户端无 KV API,由 App Server 下发
在线人数 / 成员getChatroomInfo.onlineCount、getChatroomMembers成员关系绑定 WS 会话,断连即离开
禁言 / 封禁 / 白名单Open API 管理能力需要房主/管理员角色;白名单豁免
音视频chatroom.rtc.bind 只存房间号不提供音视频能力,需外部 RTC 服务
生命周期空闲 30 分钟销毁 / 定时销毁 / 保活 3 个月销毁后 KV、禁言、白名单全部清理

整体架构​

主播端 推流 + 聊天室发言 观众端 × N 拉流 + 弹幕 + 点赞 业务服务 创建房间 / KV 公告 禁言 / 白名单 / 销毁 IM 聊天室 WS 在线广播 成员 / KV / 治理 RTC / CDN 外部服务

服务端:开播准备(Node SDK)​

JavaScript
import { AppClient } from '@ni-paas-im/server-sdk';

const app = new AppClient({ appKey: process.env.IM_APP_KEY, appSecret: process.env.IM_APP_SECRET, baseUrl: 'http://127.0.0.1:8080' });

export async function openLiveRoom({ roomId, title, anchorId, rtcRoomId }) {
  // 1) 创建聊天室(幂等;重复开播返回同一房间)
  const room = await app.createChatroom({ chatroomId: roomId, name: title, owner: anchorId });

  // 2) 定时销毁:直播结束后自动清理(这里 4 小时;活动不延长)
  await app.openApi('chatroom.destroy.policy.set', {
    chatroomId: room.chatroomId,
    destroyType: 'fixed',
    idleMs: 4 * 60 * 60 * 1000,
  });

  // 3) 公告(TTL 30 分钟)+ 榜单快照
  await app.openApi('chatroom.kv.set', {
    chatroomId: room.chatroomId,
    key: 'announcement',
    value: JSON.stringify({ text: '欢迎进入直播间,文明发言', updatedAt: Date.now() }),
    ttlMs: 30 * 60 * 1000,
  });

  // 4) 绑定音视频房间号(仅存储;音视频由外部 RTC 提供)
  await app.openApi('chatroom.rtc.bind', { chatroomId: room.chatroomId, rtcRoomId });

  // 5) 运营号进白名单:不受禁言/全员禁言影响
  await app.openApi('chatroom.whitelist.add', { chatroomId: room.chatroomId, userIds: [anchorId, 'ops_1'] });

  // 6) 低优先级消息配置:高并发时客户端折叠图片/位置
  await app.openApi('chatroom.message.priority.set', {
    chatroomId: room.chatroomId,
    messageTypes: ['image', 'file', 'location'],
  });

  return room;
}

export async function closeLiveRoom(chatroomId) {
  // 踢出全部在线成员、广播 destroyed、清理 KV/禁言/白名单、回调 chatroom.destroyed
  return app.destroyChatroom({ chatroomId });
}

客户端:进房与弹幕(Web / Android / Flutter)​

import { createClient, ConversationType, Events } from '@ni-paas/im-web';

const client = createClient({ appkey: 'YOUR_APP_KEY', serverUrl: 'http://127.0.0.1:8080' });
await client.connect(TOKEN);

client.addEventListener(Events.CHATROOM_EVENT, (event) => {
  if (event.kind === 'destroyed') showEnded(event.chatroomId);
  if (event.kind === 'member_changed') updateOnline(event.onlineCount);
});
client.addEventListener(Events.MESSAGES, ({ messages }) => {
  for (const message of messages) {
    if (message.conversationType === ConversationType.CHATROOM) appendDanmaku(message);
  }
});

await client.joinChatroom('live_1001');
await client.sendTextMessage(ConversationType.CHATROOM, 'live_1001', '主播好!');

// 公告由业务服务通过自己的通道下发(客户端无 KV API)
const announcement = await fetch('/api/live/announcement?room=live_1001').then((r) => r.json());
client.joinChatroom("live_1001", object : ResultCallback<Chatroom> {
    override fun onSuccess(room: Chatroom) {
        client.sendTextMessage(ConversationType.CHATROOM, "live_1001", "主播好!", null, object : SendMessageCallback {
            override fun onSuccess(message: Message) = Unit
            override fun onError(error: IMError) = showError(error.code, error.message)
        })
    }
    override fun onError(error: IMError) = showError(error.code, error.message)
})
await client.joinChatroom('live_1001');
await client.sendTextMessage(3, 'live_1001', '主播好!');

client.addEventListener('CHATROOM_EVENT', (event) {
  if (event.kind == 'destroyed') showEnded(event.chatroomId);
  if (event.kind == 'member_changed') updateOnline(event.onlineCount);
});

运营动作​

场景操作接口 / 控制台
刷屏用户禁言 N 分钟(mutedUntil 为 epoch ms,0 解禁)chatroom.mute
恶意用户封禁(立即踢出、不可再加入)chatroom.ban
全员禁言(抽奖/口播)muted=true;白名单豁免chatroom.mute-all
公告更新写入 KV(可带 TTL)+ 业务通道推送chatroom.kv.set
在线人数查询 onlineCount 或成员列表chatroom.get / chatroom.members.list
异常结束立即销毁并踢人chatroom.destroy / 控制台
内容审核敏感词 + 发送前回调 + 自动审核任务审核教程

容量与性能建议​

项建议
单房间在线上限 5,000;超过请拆分房间或使用多个直播间
弹幕频率每用户 120 条/分钟;高峰期在客户端做本地节流,并提示用户
消息体控制在 5KB 内;榜单/礼物动画只放 ID 与短字段
在线人数刷新1–5 秒拉一次 onlineCount,或由业务服务聚合;不要每收一条消息查一次
公告下发App Server 主动推送(KV 变更回调触发),避免客户端轮询 KV
结束清理固定销毁 + 主动销毁双保险;销毁后不要复用同一 chatroomId 做“同一场直播”

错误处理​

错误码场景处理
1012房间不存在(未创建/已过期)服务端先 chatroom.create,客户端提示“直播已结束”
1013房间已销毁禁止再加入/发言;结束页引导
1014未加入房间发言 / 被封禁后加入先 join 并等待 chatroom_joined;被封禁提示
1003非房主/管理员执行治理治理操作只走 App Server 并校验运营身份
1008发言过快客户端节流 + 退避重试
1009被禁言输入框禁用并提示解禁时间
3002join 超时(10 秒)重试 join;持续失败提示网络问题
1021聊天室服务未开通控制台开通聊天室服务

上线检查清单​

  1. 确认应用已开通聊天室服务;关闭时调用返回 1021。
  2. 房间创建、销毁策略、KV、白名单、低优先级配置全部由服务端完成,客户端不持有 AppSecret。
  3. 客户端 CHATROOM_EVENT 在 connect 前注册,处理 destroyed/kicked 并清理 UI。
  4. 断线重连后 SDK 会自动补 join;页面需要重新拉一次在线人数与公告。
  5. 敏感词与发送前回调已配置并验签;审核任务有运营入口。
  6. 压测按 5,000 在线、每人 120 条/分钟评估;客户端做节流与折叠。
  7. 结束时主动销毁 + 固定销毁兜底,确认回调 chatroom.destroyed 已收到。

相关页面​