16、LiveKit框架:LiveKit架构、部署配置、客户端SDK集成
聊到WebRTC的落地,我不得不提LiveKit。这个框架我用了快两年了,从最初的1.0版本一直跟到现在。说实话,它是我见过的把「开箱即用」和「灵活扩展」平衡得最好的开源实时通信平台之一。
你可能会问:市面上有那么多WebRTC方案,为什么偏偏要学LiveKit?嗯,我当初也有这个疑问。直到我在一个项目中需要同时支持SFU转发、录制、以及客户端快速集成——LiveKit几乎是一站式解决了。说白了,它把WebRTC底层那些复杂的信令协商、ICE穿透、媒体流管理都封装好了,你只需要关心业务逻辑。
16.1 LiveKit架构概览
先看整体架构。LiveKit的核心组件其实不多,但每个都挺关键。
核心组件一览:
- LiveKit Server:基于Go语言开发的SFU服务器,负责媒体流转发、房间管理、信令处理
- Redis:用于节点间通信、房间状态同步、分布式锁
- Ingress/Egress:分别负责媒体流输入(如RTMP推流)和输出(录制、转码)
- 客户端SDK:支持JS/React、iOS、Android、Flutter、Unity等平台
我个人习惯把LiveKit的架构理解成「三层模型」:最底层是媒体引擎(基于Pion/WebRTC),中间层是SFU路由和房间管理,最上层是业务API和事件回调。这种分层的好处是——你可以在不碰底层的情况下,快速搭建起一个生产级的实时音视频应用。
这张图我画得比较简洁,但核心逻辑都在里面了。客户端通过SDK连接LiveKit Server,Server内部通过Redis做分布式协调,媒体流走的是SFU模式——每个客户端只跟服务器建立一条连接,由服务器负责转发。这跟Mesh模式(客户端之间直连)完全不同,性能要好得多。
16.2 部署配置实战
部署LiveKit,我推荐用Docker Compose。原因很简单:依赖少,启动快,环境一致。我在生产环境也是这么干的。
16.2.1 快速部署
先创建一个 docker-compose.yml:
version: '3.8'
services:
livekit-server:
image: livekit/livekit-server:latest
ports:
- "7880:7880" # HTTP API
- "7881:7881" # WebSocket信令
- "50000-60000:50000-60000/udp" # 媒体端口
environment:
LIVEKIT_KEYS: "devkey: your-secret-key"
LIVEKIT_REDIS_ADDRESS: "redis:6379"
LIVEKIT_RTC_UDP_PORT_RANGE: "50000:60000"
depends_on:
- redis
redis:
image: redis:7-alpine
ports:
- "6379:6379"
小提示:媒体端口范围建议根据并发用户数调整。每个参与者大约需要2-4个端口。我曾经在压测时因为端口不够导致连接失败,后来改成了 50000-65000 才解决问题。
启动命令很简单:
docker-compose up -d
启动后,访问 http://localhost:7880 就能看到LiveKit的Web界面。嗯,这里要注意——默认没有认证,生产环境一定要加上API Key。
16.2.2 生产环境配置要点
我个人习惯把配置拆成几个关键块:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
LIVEKIT_KEYS |
随机生成的32位密钥 | 用于客户端Token签名 |
LIVEKIT_REDIS_ADDRESS |
Redis集群地址 | 单节点用localhost:6379,生产用哨兵或集群 |
LIVEKIT_RTC_UDP_PORT_RANGE |
50000:65000 | 根据并发量调整,每个用户约2-4个端口 |
LIVEKIT_LOG_LEVEL |
info | 调试时用debug,生产用info或warn |
避坑指南:我曾经在生产环境忘记配置 LIVEKRT_RTC_UDP_PORT_RANGE,结果默认端口范围太小,用户一多就出现「无法建立连接」的报错。排查了半天才发现是端口用完了。所以,上线前一定要根据预估并发量算好端口范围。
16.3 客户端SDK集成
客户端集成这块,我以最常用的JavaScript SDK为例。其他平台的SDK(iOS、Android、Flutter)API设计基本一致,学会了JS的,其他平台上手很快。
16.3.1 安装与初始化
先安装SDK:
npm install livekit-client
然后初始化连接:
import { Room, RoomEvent } from 'livekit-client';
const room = new Room({
adaptiveStream: true, // 自适应码流
dynacast: true, // 动态编码
});
room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
// 当有新的音视频轨道订阅时触发
const element = track.attach();
document.body.appendChild(element);
});
room.on(RoomEvent.Disconnected, () => {
console.log('房间连接已断开');
});
// 连接房间
await room.connect('wss://your-livekit-server.com', token);
console.log('已连接到房间:', room.name);
这段代码里,adaptiveStream 和 dynacast 是两个很实用的特性。前者会根据网络状况自动调整分辨率,后者会动态选择编码方式。我在一个跨国会议项目中,就是靠这两个参数把卡顿率从15%降到了3%以下。
16.3.2 发布与订阅媒体流
发布本地音视频:
// 获取本地音视频
const localVideo = await room.localParticipant.setCameraEnabled(true);
const localAudio = await room.localParticipant.setMicrophoneEnabled(true);
// 或者手动创建轨道
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
});
const videoTrack = new LocalVideoTrack(mediaStream.getVideoTracks()[0]);
await room.localParticipant.publishTrack(videoTrack);
订阅远端流其实不需要手动操作——LiveKit默认会自动订阅房间内所有参与者的音视频。你只需要监听 TrackSubscribed 事件,把轨道挂载到DOM上就行。
核心概念:LiveKit的媒体流模型是「发布-订阅」模式。每个参与者可以发布多个轨道(Track),其他参与者通过订阅来接收。SDK内部会自动处理SDP协商、ICE连接、码率适配等底层细节。
16.3.3 Token生成
连接房间需要Token。Token里包含了用户身份、房间权限、有效期等信息。生成Token的代码一般在服务端:
// 服务端Node.js示例
import { AccessToken } from 'livekit-server-sdk';
const token = new AccessToken('API_KEY', 'API_SECRET', {
identity: 'user-123',
name: '张三',
});
token.addGrant({
roomJoin: true,
room: 'my-room',
canPublish: true,
canSubscribe: true,
});
const jwt = token.toJwt();
console.log('生成的Token:', jwt);
Token的有效期我一般设成1小时。太短了用户频繁重连,太长了有安全风险。你想想看,如果Token泄露了,别人拿着你的Token就能进房间——所以有效期要合理。
16.4 实际项目中的经验总结
最后分享几个我在项目中踩过的坑:
- Redis挂了怎么办? LiveKit依赖Redis做状态同步,如果Redis宕机,新房间无法创建,但已存在的房间还能继续通信。所以生产环境一定要部署Redis集群或哨兵模式。
- 媒体端口别开太小。 前面说过了,每个用户2-4个UDP端口,1000并发至少需要4000个端口。我习惯留50%的余量。
- 客户端重连逻辑。 网络波动时,SDK会自动重连。但如果你发现重连后画面卡住,可以手动调用
room.reconnect()强制刷新连接。 - 录制功能。 LiveKit的Egress模块支持录制为MP4或WebM。但要注意——录制会消耗额外的CPU和带宽,建议单独部署Egress服务,不要跟SFU混部。
嗯,LiveKit的内容其实挺多的,但核心就是这三块:架构理解、部署配置、SDK集成。把这三点吃透了,你就能快速搭建起一个生产级的实时音视频应用。剩下的细节,比如自定义布局、数据通道、房间事件回调等,都是在这些基础上延伸出来的。