开发 WHIP Service
WHIP Service 是客户部署和运维的业务服务。最新 Go SDK 将认证、WHIP 接入、业务资源和 Echo 诊断拆成可组合的层。
1. 初始化及启动 SDK
初始化参数来自服务注册信息和部署环境:
| 参数 | 含义 |
|---|---|
serviceName | 服务注册确认的 service_name |
accessKey | 服务注册确认的 Access Key ID,用于选择验签公钥 |
publicKey | 与业务服务端签发私钥配对、并已注册的 Ed25519 公钥 |
candidateIP | 可选。服务的公网 IP。服务在 NAT 后时需要配置 |
进程级 TiRTC 引擎只初始化和启动一次。tirtcx.Init 必须在调用 tirtcx 的其他 API 之前完成:
if err := tirtcx.Init(); err != nil {
return err
}
defer tirtcx.Uninit()
verifier, err := tirtcxauth.NewEd25519TokenVerifier(map[string]string{
accessKey: publicKey,
})
// auth 是一个 http 拦截器,用于验证 Bearer Token,并将 Claims 写入请求 Context。
auth := tirtcxauth.BearerInterceptor(serviceName, verifier)
// acceptor 用于接受 SDP Offer 并创建 WHIP Session。
acceptor := tirtcx.NewWhipAcceptor(candidateIP)
// 其他初始化代码
...
// 启动
if err := tirtcx.Start(); err != nil {
return err
}
defer tirtcx.Stop()2. 认证创建请求
只在创建会话的 POST Handler 外应用 BearerInterceptor:
mux.HandleFunc("POST /whip", auth(postWHIP))
mux.HandleFunc("DELETE /whip/resource/{session_id}", deleteResource)认证成功后,可从 Context 读取设备身份:
claims, ok := tirtcxauth.ClaimsFromContext(r.Context())3. 接受 WHIP 会话
func (s *mediaService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 只接受 SDP Offer。
mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil || !strings.EqualFold(mediaType, "application/sdp") {
http.Error(w, "content-type must be application/sdp", http.StatusUnsupportedMediaType)
return
}
// 限制 SDP Offer 大小为 1 MiB。
offer, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1<<20))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 使用 128 位密码学安全随机数生成不可预测的会话 ID。
sessionID, err := newSessionID()
if err != nil {
http.Error(w, "create session ID: "+err.Error(), http.StatusInternalServerError)
return
}
// 在 30 秒内接受 SDP Offer 并生成 SDP Answer。
acceptCtx, cancelAccept := context.WithTimeout(r.Context(), 30*time.Second)
// acceptor 在初始化时创建
whipSession, err := acceptor.WhipAccept(
acceptCtx,
offer,
tirtcx.ConnEventOptions{
ErrorBuffer: tirtcx.MinEventBufferSize,
DisconnectedBuffer: tirtcx.MinEventBufferSize,
},
)
cancelAccept()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 保存会话,并在独立 Goroutine 中等待建连和处理业务媒体。
sessionCtx, cancelSession := context.WithCancel(context.Background())
session := &mediaSession{cancel: cancelSession, whipSession: whipSession}
s.mu.Lock()
if s.closed {
s.mu.Unlock()
cancelSession()
_ = whipSession.Close()
http.Error(w, "service is shutting down", http.StatusServiceUnavailable)
return
}
s.sessions[sessionID] = session
s.wg.Add(1)
s.mu.Unlock()
go s.run(sessionCtx, sessionID, session)
// 返回 201、SDP Answer 和用于删除会话的 Location。
w.Header().Set("Content-Type", "application/sdp")
w.Header().Set("Location", "/whip/resource/"+sessionID)
w.WriteHeader(http.StatusCreated)
_, _ = w.Write(whipSession.AnswerSDP())
}
func newSessionID() (string, error) {
var value [16]byte
if _, err := rand.Read(value[:]); err != nil {
return "", err
}
return hex.EncodeToString(value[:]), nil
}4. 管理连接生命周期
在独立 Goroutine 中等待连接:
connectCtx, cancelConnect := context.WithTimeout(sessionCtx, 30*time.Second)
defer cancelConnect()
conn, err := session.WaitConn(connectCtx)
if err != nil {
return
}
defer session.Close()
select {
case <-conn.Done():
if err := conn.Err(); err != nil {
// 异常终止
}
case <-sessionCtx.Done():
}WaitConn 可并发、重复调用。单个等待者超时不会关闭会话;需要结束待连接或已连接会话时,调用幂等的 WhipSession.Close()。
应用负责普通会话的注册表和 DELETE。DELETE 不做 Token 鉴权,随机会话 ID 即访问凭据。收到 DELETE <Location> 时,应先从注册表移除资源,再取消业务 Context 并关闭 WhipSession;未知或已删除的 ID 也返回 204 No Content。入口按设备能力支持 HTTP 或 HTTPS,并应限制请求速率、避免记录完整资源路径。
5. 处理事件
只为需要消费的事件配置缓冲区。启用后必须持续读取,否则缓冲区满时事件会被丢弃。
订阅决策不再通过事件通道异步返回,而是由同步 Handler 决定:
options := tirtcx.ConnEventOptions{
AudioBuffer: 16,
VideoBuffer: 16,
SubscribeVideoHandler: func(streamID uint8) int {
return 0
},
}订阅 Handler 运行在原生回调中,必须快速返回且不能阻塞。使用 Conn.EventStats() 监控事件丢弃。
6. 可选:嵌入 Echo
echo, err := whipecho.NewHTTPAdapter(
acceptor,
"/whip/echo/resource",
whipecho.HTTPOptions{},
)
mux.HandleFunc("POST /whip", auth(echo.Wrap(businessHandler)))
mux.HandleFunc(
"DELETE /whip/echo/resource/{session_id}",
echo.DeleteHandler(),
)whipecho 自动根据 _tg_mode=echo 参数拦截并管理 echo 诊断会话。_tg_* 参数为探鸽保存参数,业务不应使用。 普通请求不会被 whipecho 拦截。
7. 部署要求
部署 WHIP Service 的 Linux 主机必须将本地端口范围设置为 12768 63999:
sudo sysctl -w net.ipv4.ip_local_port_range="12768 63999"需要持久生效时,在 /etc/sysctl.d/ 下增加配置:
net.ipv4.ip_local_port_range = 12768 63999然后执行 sudo sysctl --system。主机防火墙、云安全组及上游网络设备必须开放 12768-63999 范围内的全部 UDP 端口。
8. 优雅退出
退出顺序:
- 停止接受新的 HTTP 请求。
- 关闭普通业务会话并等待 Goroutine。
- 调用
echo.Shutdown(ctx)。 - 调用
tirtcx.Stop()和tirtcx.Uninit()。
上线时监控建连成功率、建连耗时、活跃连接、异常断开、发送失败和事件丢弃。Echo 可通过 Stats() 和 Observer 接入现有监控系统。