环境 #
依赖 #
服务打包为对应系统的二进制可执行文件,没有特殊的运行依赖,但需要redis缓存数据。
- redis
Windows下载地址 (opens new window),默认安装即可
# Linux(Ubuntu)安装:
apt install redis
# MacOS安装:
brew install redis
配置 #
进入到开发者控制台下载 (opens new window)服务端
目录结构 #
下载解压,放到任意位置即可,获得如下目录结构
├── logs # 日志目录
├── config # 配置文件目录
│ ├── xxxxx.uprtc.key # 授权证书文件,在开发者控制台项目信息中下载
│ ├── log4j.json # 日志配置
│ ├── redis.json # redis客户端配置
│ ├── server.json # 服务配置
│ └── uprtc.json # uprtc相关配置
├── uprtc-server # uprtc服务可执行文件
└── worker # uprtc服务工作进程
服务配置 #
server.json配置 #
uprtc服务的Rest接口,供其他业务服务器调用,不需要暴露到外网。
uprtc服务也需要被客户端连接,如果客户端在互联网中使用,那么被客户端访问的端口要具有外网访问权限。
{
"clusters": [
{
"id": "cluster-1", // 集群ID,必须是“cluster-数字”,数字最大9,系统内保持唯一
"name": "",
"servers" : [
{
"id": "rtc-1", // 服务ID,必须是“rtc-数字”,数字最大9999,集群内保持唯一
"name": "", // 任意起名,服务名字,备注名,增加可读性维护性,在系统内部无实际意义
"serverUrl": "wss://devel.uprtc.cmcim.com:6016/", // 客户端访问的URL
"tsl": false, // 是否开启wss服务
"localhost": "192.168.6.189", // 服务器主机内网地址
"localPort" : 6017, // 服务器主机内网端口
"rtcIp": "192.168.6.189", // rtc服务IP(域名),rtc地址端口需要被客户端访问,如果为空默认为serverUrl的hostname
"rtcIpv6": "2408:832e:20a1:9f20::a23", // rtc服务Ipv6地址,如果serverUrl配置的域名可以解析成ipv6地址这里可以不用配置,如果serverUrl配置的是ipv4地址并且还需要支持ipv6访问,可以在这里配置ipv6的地址,创建房间返回的信息中会包含有serverUrl6即客户端连接的ipv6 url。建议使用域名方式。
"rtcPortScope": [49899, 49999], // rtc服务端口范围,例如 49899 ~ 49999
"recordOnly": false // 如果为true表示此服务只用来录制,默认false或者不填写此配置项
}
]
},
// {
// "id": "cluster-2",
// "name": "",
// "servers" : [
// ... 省略
// ]
// }
]
}
redis配置 #
{
"socket": {
"host": "127.0.0.1",
"port": 6379,
"connectTimeout": 10000
},
"password": "",
"database": 0,
"isolationPoolOptions": {
"max": 100,
"min": 8,
"maxWaitingClients": 10,
"idleTimeoutMillis": 10000,
"testOnBorrow": true
}
}
log4j.json配置 #
日志配置,详细参照 (opens new window)
{
"appenders": {
"console": {
"type": "console"
},
"debug": {
"type": "dateFile",
"filename": "./logs/uprtc",
"pattern": "yyyy-MM-dd.log",
"encoding": "utf-8",
"alwaysIncludePattern": true
}
},
"categories": {
"default": {"appenders": ["console", "debug"], "level": "debug"}
}
}
UPRTC配置 #
uprtc.json
{
"numWorkers" : 0, // work进程数, 如果设置为0表示进程数等于CPU核心数
"workCpuThreshold" : [50, 70], // cpu负载超过50%开始向其他work负载,如果所有work都大于70%不再负载
"logLevel" : "debug", // 日志等级
"router" : {
"mediaCodecs" : [ // 音视频编码器配置,可自行添加,但客户端需要支持添加编码器
{
"kind" : "audio",
"mimeType" : "audio/opus",
"clockRate" : 48000,
"channels" : 2
},
{
"kind" : "video",
"mimeType" : "video/VP8",
"clockRate" : 90000,
"parameters" : {
"x-google-start-bitrate" : 1000
}
},
{
"kind" : "video",
"mimeType" : "video/VP9",
"clockRate" : 90000,
"parameters" : {
"profile-id" : 2,
"x-google-start-bitrate" : 1000
}
},
{
"kind" : "video",
"mimeType" : "video/h264",
"clockRate" : 90000,
"parameters" : {
"packetization-mode" : 1,
"profile-level-id" : "42e01f",
"level-asymmetry-allowed" : 1
}
}
]
},
"room": {
"autoCrossClusterLoad": false, // 是否自动跨集群负载,false表示在集群内自动负载(可使用手动跨集群负载),true表示自动向其他集群负载
"maxIdelTime": 300, // 房间被创建后未使用超过最大空闲时间后房间自动关闭,单位秒
"maxConvoMemberNum": 0, // 多方会话房间最大人数,为0时无限制
"maxLoadDepth": 16, // 最大负载深度
"defaultLoadSubNode": 2, // 默认负载子节点数
"recordPath": "/tmp", // 录像文件存放路径
"recordPortScope": [47899, 47999], // 录像使用的端口范围
"recordCpu": 2, // 录像处理使用CPU数
"recordOriginalFileKeep": true // 是否保留录像处理的过程文件
}
}
负载树 #
maxLoadDepth, defaultLoadSubNode说明(适用于直播房间)
集群部署时,房间会自动向其他服务器负载,整体负载服务器结构呈树型,如同数据结构中的树,maxLoadDepth
表示树的最大深度,树不能超过这个深度,defaultLoadSubNode
表示每个节点的默认子节点数,当树无法超过最大深度时会按层级依次增加子节点数。maxLoadDepth
越大表示客户端连接到叶子节点时的媒体流延迟会更大,defaultLoadSubNode
越大表示每个节点的负载压力越大。
maxLoadDepth
理论值 = 可接受媒体流延迟/服务器间媒体流延迟
统一配置 #
server.json,log4j.json,uprtc.json的配置可以放到redis中,服务启动时会在redis取得配置内容。
- log4j.json以字符串方式存储,key:uprtc:config:log4j,value和log4j.json内容一致
- uprtc.json以字符串方式存储,key:uprtc:config:rtc,value和uprtc.json内容一致
- server.json以hashmap方式去存储,每个集群存储一个,参照下图:
Nginx配置集群 #
- uprtc server 会被其他业务服务器通过rest api方式调用,同时也会被客户端访问,客户端访问又分为websocket连接和rtc连接,websocket负载信令传输,业务消息通信,媒体协商,rtc负载媒体流的传输。当大规模集群部署时,需要统一的管理与访问,下面以nginx方式来说明。
- 集群部署时,调用房间操作相关接口和创建房间(房间所在)的服务器要一致
- 集群部署时,客户端需要和创建房间(房间所在)的服务器建立连接,系统内部会把客户端路由到其他负载较低的服务器。
- 域名和外网地址的配置,请到域名服务商DNS解析处配置。内网开发环境可以搭建个临时DNS服务,或者不使用https,wss直接使用ip地址也是可以的
- 拓补参照
servers.json集群配置 #
{
"clusters": [
{
"id": "cluster-1",
"name": "",
"servers" : [
{
"id": "rtc-1",
"name": "",
"serverUrl": "wss://devel.uprtc.cmcim.com:7000/s1/c1/",
"tsl": false,
"localhost": "192.168.1.10",
"localPort" : 6000,
"rtcIp": "",
"rtcPortScope": [49900, 49999]
},
{
"id": "rtc-2",
"name": "",
"serverUrl": "wss://devel.uprtc.cmcim.com:7000/s2/c1/",
"tsl": false,
"localhost": "192.168.1.11",
"localPort" : 6000,
"rtcIp": "",
"rtcPortScope": [49800, 49899]
},
{
"id": "rtc-3",
"name": "",
"serverUrl": "wss://devel.uprtc.cmcim.com:7000/s3/c1/",
"tsl": false,
"localhost": "192.168.1.12",
"localPort" : 6000,
"rtcIp": "",
"rtcPortScope": [49700, 49799]
}
]
}
]
}
- 其中serverUrl中
/s2/c1
以下称为serverPath
,是服务器ID和集群ID简化而来,s2表示rtc-2,c1表示cluster-1,下面nginx配置会用到 - 支持多平台如果包括web端时,建议开发环境就使用https,wss,和生产环境统一避免上线才发现问题,开发环境可使用自建证书并在开发者本地授信,但生产环境一定使用授信机构颁发的证书
业务服务访问uprtc server的nginx配置 #
upstream uprtc-api {
server 192.168.1.10:6000;
server 192.168.1.11:6000;
server 192.168.1.12:6000;
}
server {
listen 6200;
listen [::]:6200 ;
server_name uprtc.api;
location /uprtc/api {
proxy_pass http://uprtc-api;
}
location /s1/c1/ {
proxy_pass http://192.168.1.10:6000/;
}
location /s2/c1/ {
proxy_pass http://192.168.6.11:6000/;
}
location /s3/c1/ {
proxy_pass http://192.168.6.12:6000/;
}
}
- 业务服务器可通过192.168.1.9:6200直接访问uprtc rest api。upstream负载方式开发者可自行配置。
- 业务服务器调用创建房间接口返回数据中
serverAlias
可构建出serverPath
(例如/s1/c1)需要在下次对房间进行接口调用时拼接到地址后,以添加会话成员接口为例,http://192.168.1.9:6200/s1/c1/uprtc/api/v2/room/{roomId}/conv/members - 业务服务调用创建房间接口时无需添加
serverPath
,此时会根据nginx配置的upstream uprtc-api
负载到某个rtc主机上,房间会在此主机上被创建。 - 新增服务时在
upstream uprtc-api
下添加服务主机地址,并配置对应的location即可
客户端访问uprtc服务的nginx配置 #
server {
listen 7000 ssl http2; // 如果不需要https可以去掉"ssl http2"
listen [::]:7000 ssl http2;
server_name uprtc.client.wss;
// 证书路径按实际修改,生产环境的证书需要是授信的,自己创建的证书不适用。
// 如果不需要https,去掉下面两行配置。
ssl_certificate /etc/pki/CA/certs/uprtc.crt;
ssl_certificate_key /etc/pki/CA/req/uprtc.key;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Sec-WebSocket-Protocol "protoo";
proxy_set_header Sec-WebSocket-Version 13;
proxy_set_header Cache-Control "no-cache";
proxy_set_header Pragma "no-cache";
location /s1/c1/ {
proxy_pass http://192.168.1.10:6000/;
}
location /s2/c1/ {
proxy_pass http://192.168.6.11:6000/;
}
location /s3/c1/ {
proxy_pass http://192.168.6.12:6000/;
}
}
- 浏览器访问rtc网页需要是授信的https,websocket也需要是同源的wss,其他平台没有此约束,这是W3C制定标准不是UPRTC的限制。
- 客户端通过7000端口访问服务,端口需要路由器暴露到外网。客户端连接到房间使用的URL是创建房间返回的
serverUrl
,例如房间所在服务器为rtc-1,那么serverUrl为wss://devel.uprtc.cmcim.com:7000/s1/c1/,serverUrl就是上面rtc-1服务配置的serverUrl。 - 带邀请码的URL举例 wss://devel.uprtc.cmcim.com:7000/s1/c1/229937516,其中229937516为邀请码
- 新增服务时需要添加对应的location
客户端和uprtc server建立rtc连接 #
- 客户端需要直接和uprtc服务建立rtc连接,不建议通过nginx等其他web服务器进行中转或反向代理,会有性能上的影响。
- 以rtc-1为例子,配置的rtc端口范围49900到49999,可以路由器做端口映射把这个区间段的端口都映射给192.168.1.10(rtc-1),客户端可以通过,客户端可以通过路由器外网地址,映射的端口和rtc-1建立rtc连接
路由配置集群 #
既然RTC连接需要暴露大量端口,其他访问也直接通过暴露端口的方式建立连接。拓补参照
servers.json集群配置 #
- 通过路由器做端口映射,以rtc-1为例,把路由器7001端口映射到192.168.1.10:7001, 路由器49900 ~ 49999端口映射到192.168.1.10:49900 ~ 49999, rtc-2,3同理
- 域名和外网地址的配置,请到域名服务商DNS解析处配置。内网开发环境可以搭建个临时DNS服务,或者不使用https,wss直接使用ip地址也是可以的
{
"clusters": [
{
"id": "cluster-1",
"name": "",
"servers" : [
{
"id": "rtc-1",
"name": "",
"serverUrl": "wss://devel.uprtc.cmcim.com:7001/",
"tsl": true,
"localhost": "192.168.1.10",
"localPort" : 6000,
"rtcIp": "",
"rtcPortScope": [49900, 49999]
},
{
"id": "rtc-2",
"name": "",
"serverUrl": "wss://devel.uprtc.cmcim.com:7002/",
"tsl": true,
"localhost": "192.168.1.11",
"localPort" : 6000,
"rtcIp": "",
"rtcPortScope": [49800, 49899]
},
{
"id": "rtc-3",
"name": "",
"serverUrl": "wss://devel.uprtc.cmcim.com:7003/",
"tsl": true,
"localhost": "192.168.1.12",
"localPort" : 6000,
"rtcIp": "",
"rtcPortScope": [49700, 49799]
}
]
}
]
}
业务服务访问uprtc server #
- 创建房间时业务服务器可以先对任意服务器发起取得最小负载服务请求,会得到最小负载服务的地址信息,假设取得最小负载服务为rtc-1,创建直播房间的URL为http://192.168.1.10:6000/uprtc/api/v2/room/live
- 业务服务器需要存储房间信息,房间信息中有构建URL的必要属性,下次调用接口对房间进行操作时,直接根据房间信息构建访问的url。例如添加连麦成员,承接上条假设,取得房间信息根据地址构建的URL为http://192.168.1.10:6000/uprtc/api/v2/room/{roomId}/live/member
客户端访问uprtc websocket服务,rtc服务 #
客户端拿到房间的serverUrl
调用joinRoom即可加入到指定房间,以房间在rtc-1为例,其中配置7001端口,49900 ~ 49999端口都已通过路由器暴露给外网并映射到rtc-1,所有客户端可以直接访问UPRTC的websocket服务和rtc服务
总结和原则 #
总结
这两种集群配置的方式不是绝对,可以灵活使用,比如业务服务器调用UPRTC可以用nginx负载,客户端访问可以使用路由方式,当然开发者也可以使用它方式,无论那种方式需要遵循以下原则:
- 调用房间操作相关接口和创建房间(房间所在)的服务器要一致
- 客户端加入到房间(joinRoom)连接的服务器和创建房间(房间所在)的服务器要一致。(UPRTC内部会自动把客户端路由到其他负载较低的服务器)
- 客户端能和UPRTC服务器能建立rtc连接,即server.json中配置的rtc地址和端口能有效访问,防火墙需放行tcp,udp
跨集群负载 #
在uprtc.json配置中autoCrossClusterLoad设置为true可实现自动跨集群负载,为false时如果需要跨集群负载可采用手动方式,即代码控制。
手动跨集群负载 #
举例说明,这里假设有两个集群A,B,A部署在北方某机房,B部署在南方某机房。当某头部主播开播,应用服务器会调用UPRTC服务器创建房间,假设房间创建在A集群,开发者再调用接口为此房间创建一个在集群B的镜像房间,当客户端请求应用服务器取得房间地址时,应用服务器可判断此客户端连接哪个机房更优,则返回此房间在哪个机房的URL。集群B的镜像房间有着和集群A本体房间一样的负载能力,有独立的负载树。