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和事件回调。这种分层的好处是——你可以在不碰底层的情况下,快速搭建起一个生产级的实时音视频应用。

LiveKit 核心架构图 客户端层 Web SDK | iOS SDK | Android SDK | Flutter SDK | Unity SDK 信令与房间管理 WebSocket信令 | 房间CRUD | 参与者管理 | Token鉴权 SFU媒体转发层 Pion/WebRTC | 音视频路由 | 码率自适应 | 丢包重传 基础设施层 Redis集群 | Ingress/Egress | 录制存储 | 负载均衡

这张图我画得比较简洁,但核心逻辑都在里面了。客户端通过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);

这段代码里,adaptiveStreamdynacast 是两个很实用的特性。前者会根据网络状况自动调整分辨率,后者会动态选择编码方式。我在一个跨国会议项目中,就是靠这两个参数把卡顿率从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 实际项目中的经验总结

最后分享几个我在项目中踩过的坑:

  1. Redis挂了怎么办? LiveKit依赖Redis做状态同步,如果Redis宕机,新房间无法创建,但已存在的房间还能继续通信。所以生产环境一定要部署Redis集群或哨兵模式。
  2. 媒体端口别开太小。 前面说过了,每个用户2-4个UDP端口,1000并发至少需要4000个端口。我习惯留50%的余量。
  3. 客户端重连逻辑。 网络波动时,SDK会自动重连。但如果你发现重连后画面卡住,可以手动调用 room.reconnect() 强制刷新连接。
  4. 录制功能。 LiveKit的Egress模块支持录制为MP4或WebM。但要注意——录制会消耗额外的CPU和带宽,建议单独部署Egress服务,不要跟SFU混部。

嗯,LiveKit的内容其实挺多的,但核心就是这三块:架构理解、部署配置、SDK集成。把这三点吃透了,你就能快速搭建起一个生产级的实时音视频应用。剩下的细节,比如自定义布局、数据通道、房间事件回调等,都是在这些基础上延伸出来的。


公众号:蓝海资料掘金营,微信deep3321