GitChat·架构 | 如何从零开始搭建高性能直播平台?

前言

现在直播已经成为移动互联网时代一个新的重要流量入口,从YY、斗鱼到花椒直播,直播已经成为人们分享交流的新方式,应用场景众多,主要分为:

  • 金融类直播:金融直播可应用于实时解盘,在线专家讲座,专家在线直播技术分析、指导投资者等使用场景。

  • 大型赛事,演唱会类直播:可应用于大型演唱会,音乐会,游戏,体育赛事等类直播场景。

  • 互动类直播:娱乐类互动,如YY等。

  • 会议类直播:大型会议直播。

等4大类。在本文中,我将先从rtmp协议开始,一步步带领大家搭建一个简易高性能的直播平台。

RTMP协议详解

RTMP协议Real Time Message Protocol(实时信息传输协议)的首字母缩写,是由Adobe公司开发的一种用于解决多媒体数据传输流多路复用和分包的网络协议。它工作在TCP协议之上,因此是一种提供可靠交付的协议,在传输时不会出现丢包情况,从而保证了用户体验(QoE)。虽然TCP协议为了提供可靠交付付出了一些额外的开销做为代价,占用了一些带宽和处理器资源,但是随着网络带宽的提高和硬件的发展,这些开销会显得越来越微不足道。因此RTMP协议在为了有很好的发展前景。

官方定义:

The Real-Time Messaging Protocol (RTMP) was designed for

high-performance transmission of audio, video, and data between Adobe

Flash Platform technologies, including Adobe Flash Player and Adobe

AIR. RTMP is now available as an open specification to create products

and technology that enable delivery of video, audio, and data in the

open AMF, SWF, FLV, and F4V formats compatible with Adobe Flash

Player.

协议分类

  • RTMP协议工作在TCP之上,是应用层协议, 默认的端口是1935。

  • RTMPE在RTMP的基础上增加了加密功能。

  • RTMPT工作在HTTP之上,默认端口是80或443,可穿透防火墙。

  • RTMPS类似RTMPT,增加了TLS/SSL的安全功能。

  • RTMFP为RTMP协议的UDP版本。

虽然协议变种有很多,但实际在我们的直播应用中最常见的是原生的RTMP协议,因此本篇文章以该协议的1.0版本为基础,对其它演进协议感兴趣的同学可以关注本文的后续知识。

交互流程

RTMP的交互流程可以分为握手过程、控制命令传输与数据传输。

enter image description here

  • 握手过程

    RTMP 连接以握手开始,RTMP 握手由三个固定长度的块组成。客户端 (发起连接请求的终端) 和服务器端各自发送相同的三块。便于演示,本文将从客户端发送的这些块指定为 C0、C1 和 C2;将从服务器端发送的这些块分别指定为 S0、S1 和 S2。

    RTMP握手以客户端发送 C0 和 C1 块开始,客户端要等收到S1之后才能发送C2,客户端要等收到S2之后才能发送其他信息(控制信息和真实音视频等数据),服务端要等到收到C0之后发送S1, 服务端必须等到收到C1之后才能发送S2, 服务端必须等到收到C2之后才能发送其他信息(控制信息和真实音视频等数据)。以下为RTMP握手的时序图介绍。

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第1张图片

流程图中所提到的各种状态如下:

状态 描述
未初始化 客户端在C0中发送协议版本,如服务端支持,则回发送S0和S1,如果不能,则连接结束
版本发送 客户端等待S1包,服务端等待C1包,当接收到想要的包,客户端发送C2,服务端发送S2,此时阶段变成了ACK的发送
ACK发送 客户端和服务端分别等待S2和C2
握手完成 客户端和服务交换消息

理论上来讲只要满足以上条件,如何安排6个Message的顺序都是可以的。但在实际实现中为了尽量减少通信的次数,客户端发送C0+C1,服务端发送S0+S1+S2,再客户端在发送C2结束握手。

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第2张图片

  • 控制命令传输

    握手结束以后,RTMP协议进入控制命令传输过程,客户端通过发送connnect命令与服务器实现双向连接。连接成功后,通过发送createStream命令建立网络流。

  • 数据传输

    网络流建立成功后,推流(将直播内容推送至服务器的过程)过程会发送publish命令发布音视频内容,拉流(服务器已有直播内容,用指定地址进行拉取的过程)过程会发送play命令播放内容。

协议格式

URI格式

rtmpt://127.0.0.1/{app}/{stream_name}

  • {app}为音频/视频和其他内容定义的一个容器。
  • {stream_name}为具体的一个流名称。

消息(Message)格式

enter image description here

消息是RTMP协议中基本的数据单元,不能种类的消息包含有不能的消息类型(Message Type)。RTMP协议一共规范了十多种消息类型。其中类型为8,9的消息分别用于传输音频和视频数据。消息头包含以下信息:

  • Message Type: 消息类型,占用1个字节。

  • Length: 有效负载的字节数,占用3个字节。该字段是用大字节序表示的。

  • Timestamp: 时间戳,占用4个字节,用大字节序表示。

  • Message Stream Id: 消息流ID,标识消息所使用的流,用大字节序表示。

消息块格式

在网络上传输数据时,消息需要被拆分成较小的数据块才适合在相应的网络环境上传输。RTMP协议中规范了对消息拆分成消息块,每个消息块首部(ChunkHeader)有三部分组成:用于标识本块的ChunkBasicHeader,用于标识本块负载所属消息的ChunkMessageHeader,以及当时间戳溢出时才出现的ExtendedTimestamp。

消息分块

RTMP传输媒体数据的过程中,发送端首先把媒体数据封装成消息,然后把消息分割成消息块,最后将分割后的消息块通过TCP协议发送出去。接收端在通过TCP协议收到数据后,首先把消息块重新组合成消息,然后通过对消息进行解封装处理就可以恢复出媒体数据。

开源技术选型

目前直播服务器有开源和商业两种版本,商业版本主要又FMS(Flash Media Server)与Wowza。本文章仅针对开源版本做介绍,相应的开源项目主要分为Red5与 Nginx-Rtmp两类:

Red5

简介

GitHub:https://github.com/Red5/red5-server (1k+ stars)

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第3张图片

Red5是一个采用Java开发开源的Flash流媒体服务器。它支持:把音频(MP3)和视频(FLV)转换成播放流; 录制客户端播放流(只支持FLV);共享对象;现场直播流发布;远程调用。Red5使用RTMP, RTMPT, RTMPS, 和RTMPE作为流媒体传输协议,在其自带的一些示例中演示了在线录制,flash流媒体播放,在线聊天,视频会议等一些基本功能。

官方给出的主要特性: 
Red5 is an Open Source Flash Server written in Java that supports:

  • Streaming Video (FLV, F4V, MP4, 3GP)
  • Streaming Audio (MP3, F4A, M4A, AAC)
  • Recording Client Streams (FLV and AVC+AAC in FLV container)
  • Shared Objects
  • Live Stream Publishing
  • Remoting
  • Protocols: RTMP, RTMPT, RTMPS, and RTMPE

Additional features supported via plugin:

  • WebSocket (ws and wss)
  • RTSP (From Axis-type cameras)
  • HLS

安装与简单应用实例(Mac下安装)

前置条件(jdk已安装)。

enter image description here

  • 创建安装目录:

mkdir -p /Users/ypzdw/gitchat/rtmp/red5

  • 设置主目录环境变量:

export RED5_HOME=/Users/ypzdw/gitchat/rtmp/red5

  • 下载red5应用,并解压到RED5_HOME:

https://github.com/Red5/red5-server/releases/download/v1.0.7-RELEASE/red5-server-1.0.7-RELEASE.tar.gz

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第4张图片

目录简介:由于 Red5 是在 Tomcat 中运行的,因此 Red5 项目与普通 JAVAEE 项目结构类似 
conf:red5配置目录 
lib:存放的是一些依赖jar包 
weapps:用来存放应用程序,与tomcat下的webapps目录作用类似。

  • 运行:

cd /Users/ypzdw/gitchat/rtmp/red5 
./red5.sh &

  • 简单实例

经过前面的介绍,这里将用red5介绍一个简单的实例。打开http://127.0.0.1:5080 出现red5 管理控制台。

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第5张图片

选择”Publisher” demo,该项目提供了主播端与听课端。

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第6张图片

主播端:“1”中 Name表示流名,publish可以发布一个直播。

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第7张图片

直播收听端:“1”中Name为收听的流名;“2”中 Location为直播端地址;“3”中Log可以观察到整个直播的交流日志。

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第8张图片

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第9张图片

  • 停止应用:

cd /Users/ypzdw/gitchat/rtmp/red5 
./red5-shutdown.sh

Nginx-Rtmp

Github:https://github.com/arut/nginx-rtmp-module (5k+ stars)

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第10张图片

简介

俄罗斯人民开发的一款NGINX的流媒体插件,除了直播发布音视频流之外具备流媒体服务器的常见功能:

  • RTMP在线直播。

  • 基于HTTP的FLV/MP4 VOD点播。

  • HLS (HTTP Live Streaming) M3U8的支持。

  • 基于http的操作(发布、播放、录制)。

  • 可以很好的协同现有的流媒体服务器以及播放器一起工作。

  • 在线调用ffmpeg对流媒体进行转码。

  • H264/AAC音视频编码格式的支持。

  • Linux/BSD/MAC系统的支持。

官方承诺的功能:

  • RTMP/HLS/MPEG-DASH live streaming
  • RTMP Video on demand FLV/MP4, playing from local filesystem or HTTP
  • Stream relay support for distributed streaming: push & pull models
  • Recording streams in multiple FLVs
  • H264/AAC support
  • Online transcoding with FFmpeg
  • HTTP callbacks (publish/play/record/update etc)
  • Running external programs on certain events (exec)
  • HTTP control module for recording audio/video and dropping clients
  • Advanced buffering techniques to keep memory allocations at a minimum level for faster streaming and low memory footprint
  • Proved to work with Wirecast, FMS, Wowza, JWPlayer, FlowPlayer, StrobeMediaPlayback, ffmpeg, avconv, rtmpdump, flvstreamer and many more
  • Statistics in XML/XSL in machine- & human- readable form
  • Linux/FreeBSD/MacOS/Windows

常用指令与语法

  • Core

    rtmp

    语法:rtmp { … } 
    上下文:nginx根上下文 
    描述:保存所有 RTMP 配置的块

    server

    语法:server { … } 
    上下文:rtmp 
    描述:声明一个 RTMP 实例。 
    rtmp { 
    server { 

    }

    listen

    语法:listen (addr[:port]|port|unix:path) 
    上下文:server 
    描述:给 NGINX 添加一个监听端口以接收 RTMP 连接。 
    server { 
    listen 1935; 
    }

    application

    语法:application name { … } 
    上下文:server 
    描述:创建一个 RTMP 应用,application 名不支撑正则表达式。 
    server { 
    listen 1935; 
    application myapp { 

    }

    timeout

    语法:timeout value 
    上下文:rtmp, server 
    描述:Socket 超时。这个值主要用于写数据时。 
    timeout 60s;

    ping

    语法:ping value 
    上下文:rtmp, server 
    描述:RTMP ping 间隔。零值的话将 ping 关掉。RTMP ping 是一个用于检查活动连接的协议功能。发送一个特殊的包到远程连接,然后在 ping_timeout 指令指定的时间内期待一个回复。如果在这个时间里没有收到 ping 回复,连接断开。ping 默认值为一分钟。ping_timeout 默认值为 30 秒。 
    ping 3m; 
    ping_timeout 30s;

  • Access

    allow

    语法:allow [play|publish] address|subnet|all 
    上下文:rtmp, server, application 
    允许来自指定地址或者所有地址发布/播放。allow 和 deny 指令的先后顺序可选。 
    allow publish 127.0.0.1; 
    deny publish all; 
    allow play 192.168.0.0/24; 
    deny play all;

    deny

    语法:deny [play|publish] address|subnet|all 
    上下文:rtmp, server, application 
    描述:参考 allow 的描述。

  • Exec

    exec_push

    语法:exec_push command arg* 
    上下文:rtmp, server, application 
    描述:定义每个流发布时要执行的带有参数的外部命令。发布结束时进程终止。第一个参数是二进制可执行文件的完整路径。执行外部命令时可以使用参数替换: 
    $name - 流的名字。 
    $app - 应用名。 
    $addr - 客户端地址。 
    $flashver - 客户端 flash 版本。 
    $swfurl - 客户端 swf url。 
    $tcurl - 客户端 tc url。 
    $pageurl - 客户端页面 url。也可以在 exec 指令中定义 Shell 格式的转向符用于写输出和接收输入。

    exec_pull

    与exec_push类似,主要工作在play端。

  • Live

    live

    语法:live on|off 
    上下文:rtmp, server, application 
    描述:切换直播模式,即一对多广播。 
    live on;

    meta

    语法:meta on|off 
    上下文:rtmp, server, application 
    描述:切换发送元数据到客户端。默认为 on。 
    meta off;

    interleave

    语法:interleave on|off 
    上下文:rtmp, server, application 
    描述:切换交叉模式。在这个模式下,音频和视频数据会在同一个 RTMP chunk 流中传输。默认为 off。 
    interleave on;

    wait_key

    语法:wait_key on|off 
    上下文:rtmp, server, application 
    描述:使视频流从一个关键帧开始。默认为 off。 
    wait_key on;

    wait_video

    语法:wait_video on|off 
    上下文:rtmp, server, application 
    描述:在第一个视频帧发送之前禁用音频。默认为 off。可以和 wait_key 进行组合以使客户端可以收到具有所有其他数据的视频关键帧。然而这通常增加连接延迟。您可以通过在编码器中调整关键帧间隔来减少延迟。 
    wait_video on;

    publish_notify

    语法:publish_notify on|off 
    上下文:rtmp, server, application 
    描述:发送 NetStream.Publish.Start 和 NetStream.Publish.Stop 给用户。默认为 off。 
    publish_notify on;

    drop_idle_publisher

    语法:drop_idle_publisher timeout 
    上下文:rtmp, server, application 
    描述:终止指定时间内闲置(没有音频/视频数据)的发布连接。默认为 off。注意这个仅仅对于发布模式的连接起作用(发送 publish 命令之后)。 
    drop_idle_publisher 10s;

    sync

    语法:sync timeout 
    上下文:rtmp, server, application 
    描述:同步音频和视频流。如果用户带宽不足以接收发布率,服务器会丢弃一些帧。这将导致同步问题。当时间戳差超过 sync 指定的值,将会发送一个绝对帧来解决这个问题。默认为 300 ms。 
    sync 10ms;

    play_restart

    语法:play_restart on|off 
    上下文:rtmp, server, application 
    描述:使 nginx-rtmp 能够在发布启动或停止时发送 NetStream.Play.Start 和 NetStream.Play.Stop 到每个用户。如果关闭的话,那么每个用户就只能在回放的开始和结束时收到这些通知了。默认为 on。 
    play_restart off;

  • Record

    record

    语法:record [off|all|audio|video|keyframes|manual]* 
    上下文:rtmp, server, application, recorder 
    描述:切换录制模式。流可以被记录到 flv 文件。本指令指定应该被记录的: 
    off - 什么也不录制 
    all - 音频和视频(所有) 
    audio - 音频 
    video - 视频 
    keyframes - 只录制关键视频帧 
    manual - 用不自动启动录制,使用控制接口来启动/停止 
    在单个记录指令中可以有任何兼容的组合键。 
    record all;

    record_path

    语法:record_path path 
    上下文:rtmp, server, application, recorder 
    描述:指定录制的 flv 文件存放目录。 
    record_path /tmp/rec;

    record_suffix

    语法:record_suffix value 
    上下文:rtmp, server, application, recorder 
    描述:设置录制文件后缀名。默认为 ‘.flv’。 
    record_suffix _recorded.flv; 
    录制后缀可以匹配 strftime 格式。以下指令 
    record_suffix -%d-%b-%y-%T.flv 
    将会产生形如 mystream-24-Apr-13-18:23:38.flv 的文件。所有支持 strftime 格式的选项可以在 strftime man page 里进行查找

    record_append

    语法:record_append on|off 
    上下文:rtmp, server, application, recorder 
    描述:切换文件附加模式。当这一指令为开启是,录制时将把新数据附加到老文件,如果老文件丢失的话将重新创建一个。文件中的老数据和新数据没有时间差。默认为 off。 
    record_append on;

  • Relay

    pull

语法:pull url [key=value]* 
上下文:application 
描述:创建 pull 中继。流将从远程服务器上拉下来,成为本地可用的。仅当至少有一个播放器正在播放本地流时发生。 
Url 语法:[rtmp://]host[:port][/app[/playpath]]。如果 application 找不着那么将会使用本地 application 名。如果找不着 playpath 那么就是用当前流的名字。 
支持以下参数: 
app:明确 application 名。 
name:捆绑到 relay 的本地流名字。如果为空或者没有定义,那么将会使用 application 中的所有本地流。 
tcUrl:如果为空的话自动构建。 
pageUrl:模拟页面 url。 
swfUrl:模拟 swf url。 
flashVer:模拟 flash 版本,默认为 ‘LNX.11,1,102,55’。 
playPath:远程播放地址。 
live:切换直播特殊行为,值:0,1。 
start:开始时间。 
stop:结束时间。 
static:创建静态 pull,这样的 pull 在 nginx 启动时创建。 
如果某参数的值包含空格,那么你应该在整个 key=value 对周围使用引号,比如:’pageUrl=FAKE PAGE URL’。 
pullrtmp://cdn2.example.com/another/a?b=1&c=d pageUrl=http://www.example.com/video.html swfUrl=http://www.example.com/player.swf live=1;

push

语法:push url [key=value]* 
上下文:application 
描述:push 的语法和 pull 一样。不同于 pull 指令的是 push 推送发布流到远程服务器。

push_reconnect

语法:push_reconnect time 
上下文:rtmp, server, application 
描述:在断开连接后,在 push 重新连接前等待的时间。默认为 3 秒。 
push_reconnect 1s;

session_relay

语法:session_relay on|off 
上下文:rtmp, server, application 
描述:切换会话 relay 模式。在这种模式下连接关闭时 relay 销毁。当设置为 off 时,流关闭,relay 销毁,这样子以后另一个 relay 可以被创建。默认为 off。 
session_relay on;

  • Notify

    on_connect

    语法:on_connect url 
    上下文:rtmp, server 
    描述:设置 HTTP 连接回调。当客户分发连接命令一个连接命令时,一个 HTTP 请求异步发送,命令处理将被暂停,直到它返回结果代码。当 HTTP 2XX 码(成功状态码)返回时,RTMP 会话继续。返回码 3XX (重定向状态码)会使 RTMP 重定向到另一个从 HTTP 返回头里获取到的 application。否则(其他状态码)连接丢弃。 
    注意这一指令在 application 域是不允许的,因为 application 在连接阶段还是未知的。 
    HTTP 请求接收到一些参数。在 application/x-www-form-urlencoded MIME 类型下使用 POST 方法。以下参数将被传给调用者: 
    call=connect。 
    addr - 客户端 IP 地址。 
    app - application 名。 
    flashVer - 客户端 flash 版本。 
    swfUrl - 客户端 swf url。 
    tcUrl - tcUrl。 
    pageUrl - 客户端页面 url。 
    除了上述参数以外,所有显式传递给连接命令的参数也由回调发送。你应该将连接参数和 play/publish 参数区分开。播放器常常有独特的方式设置连接字符串不同于 play/publish 流名字。

    on_play

    语法:on_play url 
    上下文:rtmp, server, application 
    描述:设置 HTTP 播放回调。每次一个客户分发播放命令时,一个 HTTP 请求异步发送,命令处理会挂起 - 直到它返回结果码。之后再解析 HTTP 结果码。 
    HTTP 2XX 返回码的话继续 RTMP 会话。 
    HTTP 3XX 返回码的话 重定向 RTMP 到另一个流,这个流的名字在 HTTP 返回头的 Location 获取。 
    HTTP 请求接收到一些个参数。在 application/x-www-form-urlencoded MIME 类型下使用 POST 方法。以下参数会被传送给调用者: 
    call=play。 
    addr - 客户端 IP 地址。 
    app - application 名。 
    flashVer - 客户端 flash 版本。 
    swfUrl - 客户端 swf url。 
    tcUrl - tcUrl。 
    pageUrl - 客户端页面 url。 
    name - 流名。

    on_publish

    语法:on_publish url 
    上下文:rtmp, server, application 
    描述:同上面提到的 on_play 一样,唯一的不同点在于这个指令在发布命令设置回调。不同于远程 pull,push 在这里是可以的。

    on_done

    语法:on_done url 
    上下文:rtmp, server, application 
    描述:设置播放/发布禁止回调。上述所有适用于此。但这个回调并不检查 HTTP 状态码。

    on_play_done

    语法:on_publish_done url 
    上下文:rtmp, server, application 
    描述:等同于 on_done 的表现,但只适用于播放结束事件。

    on_publish_done

    语法:on_publish_done url 
    上下文:rtmp, server, application 
    描述:等同于 on_done 的表现,但只适用于发布结束事件。

    on_record_done

    语法:on_record_done url 
    上下文:rtmp, server, application, recorder 
    描述:设置 record_done 回调。除了普通 HTTP 回调参数它接受录制文件路径。 
    on_record_done http://example.com/recorded;

    on_update

    语法:on_update url 
    上下文:rtmp, server, application 
    描述:设置 update 回调。这个回调会在 notify_update_timeout 期间调用。如果一个请求返回结果不是 2XX,连接禁止。这可以用来同步过期的会话。追加 time 参数即播放/发布调用后的秒数会被发送给处理程序。 
    on_update http://example.com/update;

    notify_update_timeout

    语法:notify_update_timeout timeout 
    上下文:rtmp, server, application 
    描述:在 on_update 回调之间的超时设置。默认为 30 秒。 
    notify_update_timeout 10s; 
    on_update http://example.com/update;

    notify_update_strict

    语法:notify_update_strict on|off 
    上下文:rtmp, server, application 
    描述:切换 on_update 回调严格模式。默认为 off。当设置为 on 时,所有连接错误,超时以及 HTTP 解析错误和空返回会被视为更新失败并导致连接终止。当设置为 off 时只有 HTTP 返回码不同于 2XX 时导致失败。 
    notify_update_strict on; 
    on_update http://example.com/update;

    notify_relay_redirect

    语法:notify_relay_redirect on|off 
    上下文:rtmp, server, application 
    描述:使本地流可以重定向为 on_play 和 on_publish 远程重定向。新的流名字是 RTMP URL 用于远程重定向。默认为 off。 
    notify_relay_redirect on;

    notify_method

    语法:notify_method get|post 
    上下文:rtmp, server, application, recorder 
    描述:设置 HTTP 方法通知。默认是带有 application/x-www-form-urlencoded 的 POST 内容类型。在一些情况下 GET 更好,例如如果你打算在 nginx 的 http{} 部分处理调用。在这种情况下你可以使用 arg_* 变量去访问参数。 
    notify_method get; 
    Statistics 
    statistics 模块不同于本文列举的其他模块,它是 NGINX HTTP 模块。因此 statistics 指令应该位于 http{} 块内部。

    rtmp_stat

    语法:rtmp_stat all 
    上下文:http, server, location 
    描述:为当前 HTTP location 设置 RTMP statistics 处理程序。RTMP statistics 是一个静态的 XML 文档。可以使用 rtmp_stat_stylesheet 指令在浏览器中作为 XHTML 页面查看这个文档。 
    http { 
    server { 
    location /stat { 
    rtmp_stat all; 
    rtmp_stat_stylesheet stat.xsl; 

    location /stat.xsl { 
    root /path/to/stat/xsl/file; 


    }

    rtmp_stat_stylesheet

    语法:rtmp_stat_stylesheet path 
    上下文:http, server, location 
    描述:添加 XML 样式表引用到 statistics XML 使其可以在浏览器中可视。

测试

我所在的公司的直播业务中,前期也是采用red5,但是随着用户数的不断增长,red5完全不能支撑整个业务。问题集中爆发在几个方面:

  • 对于单主播,听者超过400人时,CPU超过90%(主机为4核,32G)。

  • 人数越多,音质,画面卡顿很多,不稳定,用户体验很差。于是我们决定对red5进行替换,对各种选型进行了调研,并在red5相同环境下做了测试,发现nginx-rtmp的性能非常突出,最终选用nginx-rtmp替换Red5,到目前为止,已经无故障运行近一年。附nginx-rtmp测试数据:

Server CPU 内存 连接数 带宽 延迟
nginx-rtmp 8.3% 13MB 500 100Mbps 0.8秒
nginx-rtmp 27.3% 19MB 1000 200Mbps 0.8秒
nginx-rtmp 50.2% 37MB 2500 500Mbps 0.8秒
nginx-rtmp 70.2% 61MB 4000 650Mbps 0.8秒

从测试结果可以得知,nginx-rtmp模块运行稳定,单CPU4000人时负载只有70%,已经接近网卡流量的极限,比Red5 在性能上高一个数量级。

快速实现一个直播平台

实战前准备

  • 硬件

阿里云ECS: CPU:2核心,内存:8G,硬盘:40G

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第11张图片

  • 操作系统

CentOS 7.2 x86_64 linux

  • 配置服务器

    超过1024的连接数测试需要打开linux的限制。且必须以root登录和执行

    设置连接数:ulimit -HSn 10240

    查看连接数:

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第12张图片

  • 域名

    非必须,如没有,可以直接使用ip也可以。但是正式环境中为了减少收听端对ip地址的依赖性,一般会使用域名而非ip地址来连接直播服务器。本文使用域名为datahq.cn.

    申请域名:国内可以选择万网或新网;国外的name.com和godaddy.com口碑不错。

    域名备案:国外的服务器和网站在上线前都需要经过工信部备案和公安部备案;如果在阿里云上购买ECS可以直接使用其的免费备案服务。

    建立直播子域名:

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第13张图片

  • 客户端

    为方便测试,本文使用Red5 作为直播的客户端

  • 直播端

    目前最好用的直播端软件是OBS(Open Broadcaster Software)。下载地址是:

    https://obsproject.com/download

    快速设置:

    视频的清晰度与码率和品质有关,码率大,品质高,那么视频的清晰度就高,同时,对带宽的要求也越大。详细的参数设定参考如下:

  • 在来源中新增“视频捕捉设备”,并设置分辨率,您可以从分辨率选择最接近的一项。本例分辨率为1280x720,如下图所示

  • 通过“设置”->“视频”中设置压缩分辨率,您可以从压缩分辨率选择与自己期望最接近的一项。本例期望分辨率为960x540,如下图所示

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第14张图片

  • 直播推流设置,打开“设置”->”流”,URL输入推流地址的URL,流名称输入推流的名称,如下图所示

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第15张图片

实战

  • 架构方案

    目前,我们线上的直播架构为:

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第16张图片

  • 支撑线上峰值近10万人,并无故障运行一年有余。

  • 配置中心会定期刷新直播端与收听端APP的路由信息。

  • 直播端APP推流到Master集群。

  • 由于nginx-rtmp本身不支持集群,因此我们在架构时没有采用从Master集群Forward推流到Slave集群的方式,而是设计了当收听端拉流到slave服务器集群时,如果不存在该流,就会从Master集群主动拉取流的架构,解决了直播集群的大规模并发问题。

  • 该架构在大规模并发情况下,比Master推流到Slave流的架构节省了很大的带宽。

  • 除此之外我们对nginx-rtmp进行了源代码的修改,支撑了一些如合成,转码,高级录制等功能。

本文为了各位同学能够更快的掌握如何搭建的过程,因此没有采用以上的架构,相信通过下面的实践,各位也能够搭建相应的架构。

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第17张图片

架构图中黄色标识了我们要使用的直播server。

  • 编译与部署

    创建源码存储目录:mkdir -p /root/rtmp/src

    进入源码目录:

cd /root/rtmp/src

下载nginx源码并解压,注意nginx-rtmp对nginx版本的选择限制较多,在选择时为了少踩不需要的坑,建议根据官方提示选择对应的nginx 版本,如图本文选择nginx-1.11.5:

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第18张图片

wget http://nginx.org/download/nginx-1.11.5.tar.gz 
tar -zxvf nginx-1.11.5.tar.gz

下载nginx-rtmp模块源码:

wget https://github.com/arut/nginx-rtmp-module/archive/v1.1.10.tar.gz 
tar -zxvf v1.1.10.tar.gz

编译nginx,如果需要调试消息则打开–with-debug:

./configure –add-module=/root/rtmp/src/nginx-rtmp-module-1.1.10 –with-debug 
make 
make install

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第19张图片

默认编译到路径:

/usr/local/nginx
  • 1
  • 2
  • 1
  • 2

验证编译是否成功:

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第20张图片

创建录制文件存储目录:

mkdir -p /usr/local/nginx/files
  • 1
  • 1

rtmp 模块配置(nginx.conf):

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第21张图片

本文所采用的配置如下

user  root;//使用root用户运行nginx
worker_processes  1;//指明了nginx要开启的进程数,一般等于cpu的总核数,如果没有出现io性能问题,最好不要修改

error_log  logs/error.log  debug;//错误日志存放路径;日志级别为debug,调试用。
events {
    worker_connections  1024;//每个工作进程的最大连接数量;
}


http {
    include       mime.types;//设定mime类型,类型由mime.type文件定义
    default_type  application/octet-stream;
    client_max_body_size 3m;//设定通过nginx上传文件的大小
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';//日志格式设置
    access_log  logs/access.log  main;//访问日志存储路径
    sendfile        on;//指定 nginx 是否调用sendfile 函数(zero copy 方式)来输出文件
    keepalive_timeout  65;//keepalive超时时间
    server {//配置虚拟机
        listen       8080;//监听端口
        server_name  rtmp.datahq.cn 59.110.237.245;//名称
    location /stat{//配置统计页面路径
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }
    location /stat.xsl{//统计模板路径
            root /usr/local/nginx/conf;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
rtmp{
    server{
        listen 0.0.0.0:1935;//监听端口
        ping 30s;//活动连接检查周期
        application live{//应用名
        live on;//打开直播模式
        meta copy; //是否发送直播端元数据信息
        session_relay on;//打开会话转发模式
        drop_idle_publisher 10s;//10s没有推流,自动断开直播端
        sync 10ms;//同步流时间阀值
        record_append on;//打开直播流录制追加模式
        record_path /usr/local/nginx/files;//录制文件地址
        record all;//打开录制功能
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

拷贝统计模板到ngin配置目录:

cp /root/rtmp/src/nginx-rtmp-module-1.1.10/stat.xsl /usr/local/nginx/conf/
  • 1
  • 1

启动直播服务器:

cd /usr/local/nginx/sbin && ./nginx
  • 1
  • 1

打开统计页面控制台:

http://rtmp.datahq.cn:8080/stat
  • 1
  • 1

GitChat·架构 | 如何从零开始搭建高性能直播平台?_第22张图片

统计表各属性说明为: 
clients:连接数 
live streams:流名 
codec:编码 
bits:分辨率 
size:视频画面大小 
fps:每秒传输帧数 
freq:音频率 
chan:音频声道 
State:流状态 
Time:流活动时间 
其它4个为输入与输出流的每秒传输速率。

  • 测试直播

    打开OBS,推流至rtmp://rtmp.datahq.cn/live,流名为demo

    打开red5,从rtmp://rtmp.datahq.cn/live拉取直播流,流名为demo

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第23张图片 
    打开直播统计后台:如果看到已经有一个推流,一个拉流,并有数据传输时,说明整个直播链路已经畅通。

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第24张图片

    查看直播录制文件:

    enter image description here

    此时看下系统的负载:可以发现CPU,内存的负载都很低,可以忽略不计。

    GitChat·架构 | 如何从零开始搭建高性能直播平台?_第25张图片

  • 上线

    到本节为止,一个简单的直播平台就搭建好了,接下来我们要做的就是上线,因为没有上线,一切都是零。下面是我们在上线过程中遇到的一些坑,供各位同学参考:

    • 系统打开文件数默认太低,造成部分用户连接不上。

    • 遇到活动高峰,网络带宽成为瓶颈,造成收听用户卡顿很多。在生产环境中需要时刻关注是否升级带宽。

    • 上线尽量选择在没有直播的时候进行。

总结

今天给大家分享了直播平台搭建的一些知识,涉及到了rtmp协议,直播选型的一些注意点,大规模直播架构,nginx-rtmp直播服务器实战搭建等。如果各位有直播的需要,可以顺着本文的一些知识点进行实战。

第一次写Chat,不知道效果如何,如果大家有兴趣,我们在后续推出nginx模块开发与nginx-rtmp的分享,感谢大家的参与。

你可能感兴趣的:(GitChat·架构 | 如何从零开始搭建高性能直播平台?)