高性能流媒体服务器nginx-http-live-module

12 篇文章 6 订阅
4 篇文章 0 订阅

        nginx-rtmp-module实现了开创性的工作,但是,实际上它的完成度并不高,缺少的功能和很多缺陷在前面的文章中已有提及。如果要在实际生产环境中使用,必须进行大量的修改。

        nginx-http-flv-module部分解决了一些问题,由于我自身有些知识和测试环境所限,有些问题的解决周期很长甚至无法解决。不管怎样,nginx-http-flv-module能成长成今天这个样子,离不开contributors(github里的统计不全,其实加我自己一共有8人)的贡献和网友们的测试和反馈。

        nginx-http-flv-module的vhost功能之前一直不能完美在多进程模式下运行,这个问题从有网友反馈到现在已经有快一年的时间了,期间我在CSDN,OSChina,segmentfault,Google Group,知乎甚至脉脉(还被怼了,说我发广告)都求助过,一直没有反馈,直到最近才彻底解决了这个问题。

        另外,前面的文章分析过nginx-rtmp-module在多进程模式下auto push功能有效率低下的问题,不过考虑到一般情况下publishers要比subscribers少很多,在一般场景下,nginx-rtmp-module可以胜任了。服务器压力测试表明,推流2000基本上是极限,下文会给出压力测试录屏的截图(推流还没完全稳定时,有一小段时间各个CPU的使用率都是100%)。

        但是auto push本身有其优点:在开启idle_streams选项条件下,是允许播放端在推流端还没接入之前就接入的。所以,在开启auto push时,如果推流端断开了,一定时间内重新连接,推流被自动relay到其他的进程,播放端还可以继续播放。修改过的模块,开启cond pull(我自己起的名字)时,其他条件与上述保持一致,如果推流断开了,一定时间内重新连接,由于这时接受推流的进程可能与之前的进程不同,它不会自动relay到其他进程,所以播放端不能恢复播放了。所以选择auto push还是cond pull,需要综合考量推流端,服务端和播放端的使用场景。

        注意:idle_streams默认为打开的,如果显式设置为关闭,那么播放端在推流端还没接入之前是不能接入的,并且推流端断开时,会关闭播放端。

        nginx-http-flv-module这个名称当初没如何考虑就起了,后来发现跟Nginx自带的ngx_http_flv_module的名称有点混淆,还有几个网友问过它们有什么区别。上述的一些问题在新的项目中已经解决,鉴于上述容易产生混淆的问题,并且新加的一些功能已经超出http-flv的概念,所以新项目起名为nginx-http-live-module,但是由于一些主客观原因,这个项目不再开源也不免费(HTTP2-FLV和WebSocket-FLV都已经有公司商用),至于为什么不再开源也不免费,有些人心里应该有X数。有需要的可以通过文末的联系方式跟我联系。nginx-http-flv-module我会继续维护,nginx-http-live-module的有些功能,过一段时间后我会考虑移到nginx-http-flv-module。

        不只一个网友问起过,网上宣传SRS各种指标都比nginx-rtmp-module好,nginx-http-flv-module作为在nginx-rtmp-module基础上开发的模块,有什么优势呢?不敢说nginx-http-flv-module有什么优势,但至少补齐了HTTP-FLV,GOP缓存,VHOST(在nginx-http-flv-module上没完全解决,但在nginx-http-live-module上已经完全解决了)等功能,解决了nginx-rtmp-module很多已知的bug。硬要说优势的话,有两点:1.Nginx本身依赖的第三方软件很少,如果不想依赖任何第三方软件,可以通过configure去掉;2.Nginx本身担当了反向代理和负载均衡的角色,不用维护额外的软件。对于nginx-http-live-module来说,还可以加一条:3.并发性能非常高,一只猛虎也比不上一群恶狼(特别是支持SO_REUSEPORT的系统上,Nginx开多进程是没有惊群问题的)。不过Nginx作为流媒体服务器,有个劣势,就是内存占用太高,每个进程都有自己的内存拷贝,不能共享。

        nginx-http-live-module的演示视频:高性能流媒体服务器nginx-http-live-module

        nginx-http-live-module除了nginx-http-flv-module已有的功能以外,还增加了一些功能,解决了一些问题:

1.重写http-flv的发送接口,完全调用Nginx本身提供的发送HTTP回复的接口,增强兼容性。

2.增加对HTTP/2.0的支持,解决浏览器限制同源的HTTP/1.1连接并发个数(典型值为6个)的问题

3.vhost功能不能完美在多进程模式下运行,nginx-rtmp-module的stat.xsl不支持vhost的bug,增加stat参数,可以按照参数查看指定的stat数据。

4.auto push功能在多进程模式下效率低下。

5.多进程模式下pull同源合并。

6.支持http-fmp4,有了它,浏览器不再需要任何插件就可以播放(前提是要支持MSE,Safari?呵呵),即把在浏览器的转换工作(如flv.js将http-flv等转换为fmp4)放到服务器上完成。目前浏览器(只测试过Chrome和Firefox),vlc和ffplay都播放良好,之前浏览器播放的问题已经确认是对fmp4某种格式支持不好。

7.支持fmp4封装格式的HLS(实验性),最新版的vlc播放良好,其他播放器还未测试。说点题外话,已经有好几个网友建议我在nginx-http-flv-module中支持H.265(HEVC),苹果最新的HLS规范要求如果视频编码是H.265,那么封装格式必须是fmp4(H.264编码的视频则可以封装成fmp4或者ts),详情见HLS spec中的General Authoring Requirements一节描述。还有网友贴出网上已有的支持H.265的代码,希望我整合进nginx-http-flv-module,很遗憾看到那些HLS部分的代码还是基于ts封装格式的,所以按照HLS最新规范的描述,这些支持H.265的代码是无效的。其实要HLS支持H.265只有一步之遥,跟H.264的fmp4封装格式差异很小,但是由于HLS切片是由推流驱动的,而FLV的规范不支持H.265,造成无法在nginx-http-flv-module中支持H.265视频编码的HLS。技术上其实已经没有问题了,就差一个描述H.265的ID的规范文件。SRS的作者几年前曾询问过FFmpeg社区是否有让FLV支持H.265的计划,得到的回复是除非Adobe(FLV相关技术的所有者)更新规范,否则不会支持。我个人的想法是,现在很多浏览器都越来越不支持Flash了,Adobe也曾宣布到2020年底就不支持Flash了,所以估计他们也没动力去更新规范,给他人做嫁衣。国内有让FLV/RTMP支持H.265的方案,ID是私有的,也没什么问题。还是等Adobe更新规范吧(希望不大)。

8.支持rtmps,即加密的rtmp,Facebook从今年5月1日起将所有的rtmp直播强制改为rtmps。可以作为服务器和客户端(下游服务器)。

9.支持tracing id(需要客户端传递tracing id,否则服务器日志不记录),在存在集群或者多级服务器时,可以根据tracing id迅速定位问题出现在哪个地方。

10.支持RTMP 302重定向,最近才发现Nginx本身的if并不支持“>”,“<”,“>=”,“<=”运算符。有个常见的场景,当Nginx的推拉流活动连接数已经超过某个阈值,那么可以将后来的请求重定向到另一个地址,从而减小本机的压力。那么可以使用if实现以下的重定向:

if ($connections_active > 20000) {
    return 302 rtmp://another_host[:another_port]/another_app[/another_stream];
}

        参考Nginx的代码实现了if的“>”,“<”,“>=”,“<=”,目前仅支持$connections_active变量(需要在编译时加上--with-http_stub_status_module选项),并实现了RTMP 302重定向转HTTP 302重定向的功能。

        也可以直接禁止访问:

if ($connections_active > 20000) {
    return 403;
}

2020-03-16:现在已支持不依赖于ngx_http_stub_status_module的3个变量:

#当播放连接大于等于10000时,不再接受播放请求
if ($connections_play >= 10000) {
    return 403;
}
#当推流连接数大于等于10000时,不再接受推流请求
if ($connections_publish >= 10000) {
    return 403;
}
#当总的连接数(推流连接数和播放连接数)大于等于10000时,不再接受请求
if ($connections_all >= 10000) {
    return 403;
}

另外,由于nginx-http-live-module已经支持rtmps,如果要像Facebook那样强制推流时必须使用rtmps,怎么办呢?nginx-http-live-module支持像http那样的http->https的rewrite功能(需要客户端支持,librtmp(即rtmpdump)已经原生支持rtmps,但是要支持302重定向,需要对librtmp进行修改):

rtmp {
    server {
        listen 1935;
        server_name some.domian.name;

        return 302 rtmps://some.domain.name/app[/stream];
    }

    server {
        listen 443 ssl;
        server_name some.domain.name;

        ssl_certificate /path/to/server.certificate;
        ssl_certificate_key /path/to/server.private.key;
        ...

        application app {
            live on;
        }
    }
}

#同时,return或者rewrite也支持$scheme, $host, $server_port, $request_uri等变量

11.增加对WebSocket的支持。为什么要支持Websocket,其实部分原因跟为什么要支持HTTP/2.0差不多。在flv.js的issue列表中看到有网友提到浏览器对HTTP/1.1连接并发个数的限制问题,有网友回复使用WebSocket解决了。于是在网上搜索了一下主流浏览器对WebSocket连接并发个数的限制,都是200+,这比对HTTP/1.1连接并发限制个数为6要大多了。实测WebSocket连接数限制确实不止6个,但是没有测试上限是否与网上的说法一致。用WebSocket有个好处,浏览器实现没有强制要求加密,但是由于WebSocket的RFC(rfc6455)中提到不要求客户端遵循HTTP的一些状态码规则(如3xx),所以可能有些客户端可能会无视这些HTTP状态码。Nginx本身支持代理WebSocket,但是没见到有用WebSocket来实现WebSocket-FLV的Nginx第三方模块,nginx-http-live-module的WebSocket-FLV功能目前测试工作良好。

        众所周知,浏览器对同源的HTTP/1.1连接并发个数有限制,一般为6个。所以,要在一个浏览器中打开大于6个播放连接,要么使用域名分片,要么使用HTTP/2.0,或者使用WebSocket。

        虽然HTTP/2.0的RFC没有要求对HTTP/2.0请求强制加密,但是目前的浏览器和服务器的实现都要求强制加密。Firefox从版本36.0.0开始支持HTTP/2.0;OpenSSL从版本1.0.2开始支持HTTP/2.0,CentOS 7.x自带的OpenSSL满足要求;Nginx从版本1.9.5开始支持HTTP/2.0。

        下面是使用HTTP/2.0的HTTP-FLV直播录屏截图,播放器使用的是Firefox+flv.js(由于录屏软件的CPU使用率很高,所以只开了9个播放窗口,其实测试12个都没有问题,最多能支持多少未知):

第1个播放窗口
第2个播放窗口
第3个播放窗口
第4个播放窗口
第5个播放窗口
第6个播放窗口
第7个播放窗口
第8个播放窗口
第9个播放窗口

        另外,Arut原来在支持多进程的时候,发布过一个补丁,详情见per-worker-listener,但是随着Nginx本身的迭代,这个补丁已经不能用在比较新的Nginx版本上,所以我根据这个补丁,在nginx-1.12.2的基础上加入了这个功能,并修复了几个bug,测试截图如下:

携带9100端口的url查看Nginx的第1个worker进程的stat
携带9101端口的url查看Nginx的第2个worker进程的stat

        Nginx绝大多数情况下是以多进程方式运行的,得益于Nginx良好的基础设施,nginx-http-live-module才可能解决auto push功能在多进程模式下效率低下的问题。关于auto push的问题,在nginx-rtmp-module的缺陷分析已经论述过,其推流数跟CPU个数无关,即不能通过增加CPU的个数来提高推流并发量。如果按照下文中的开启auto push时的压力测试结果,一个进程能处理推流2000个连接(按照nginx-rtmp-module的缺陷分析的分析,使用auto push时,每个进程处理的推流连接数其实是总的推流连接数),那么修改过的版本能处理的推流连接数至少是40000个。当然,推流数跟CPU个数,内存和网卡带宽有关。压力测试结果见下文截图。

        好像各大网站都不太支持上传视频,下面给出nginx-http-live-module在多进程模式下使用原生的auto push和cond pull(我自己起的名)的压力测试对比录屏截图。

服务器配置:20个CPU(Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz),64G内存(16G x 4,DDR4,2400MT/s),操作系统CentOS 7.6。

压力测试工具:srs-bench

压力测试视频:srs-bench自带的视频,视频编码H.264,音频编码AAC,码率240kbps左右。

Nginx配置:20个worker进程,开启CPU绑定,支持SO_REUSEPORT选项,无惊群效应。

1.首先是开启原生的auto push后的压力测试录屏截图:

软硬件基本信息
刚启动后的Nginx信息
推流2000路
推流2000路稳定后的Nginx信息
拉流1000路mystream_999
拉流1000路mystream_999后的Nginx信息
拉流1000路mystream_1999
拉流1000路mystream_1999后的Nginx信息
拉流1000路mystream_999的统计信息
拉流1000路mystream_1999的统计信息
推流2000路和拉流2000路稳定后的Nginx信息

2.然后是开启cond pull后的压力测试录屏截图:

2019-07-12更新:srs-bench官方说明srs-bench可模拟的客户端数量为1000~3000,详情见Client,所以原来的压力测试用srs-bench模拟8000个客户端推流可能存在问题。因为auto_push的压力测试与原来的数据差别很小,所以没有重新对auto_push的压力测试进行录屏。重新对cond_pull压力测试时,用4个srs-bench各自模拟2000个推流客户端,得到的数据与原来的压力测试数据存在一些差异(主要是CPU使用率差异,原来的方式10%不到,现在的方式是17%左右),所以把旧的压力测试截图删除了,新的压力测试截图如下:

软硬件基本信息
刚启动后的Nginx信息
推流2000路mystream1_{i}的统计信息
推流2000路mystream2_{i}的统计信息
推流2000路mystream3_{i}的统计信息
推流2000路mystream4_{i}的统计信息
推流8000路稳定后的Nginx信息
rtmp方式拉流1000路mystream1_999的统计信息
rtmp方式拉流1000路mystream1_1999的统计信息
rtmp方式拉流1000路mystream2_999的统计信息
rtmp方式拉流1000路mystream2_1999的统计信息
http-flv方式拉流1000路mystream3_999的统计信息
http-flv方式拉流1000路mystream3_1999的统计信息
http-flv方式拉流1000路mystream4_999的统计信息
http-flv方式拉流1000路mystream4_1999的统计信息
推流8000路和拉流8000路稳定后的Nginx信息(1)

推流8000路和拉流8000路稳定后的Nginx信息(2)
推流8000路和拉流8000路稳定后的Nginx信息(3)

注:后期压力测试过10000推流+10000播放,同样的软硬件环境,测试数据是CPU使用率平均为19%~21%,内存使用是31.5G(不带内存问题检测的选项时是23G左右)。

nginx-http-live-module目前已经支持在stat里查看录制的信息,这个功能也添加到nginx-http-flv-module了,如下图所示:

stat里的录制信息

联系方式:

已删。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值