srs流媒体服务器windows_聊聊CDN与高性能流媒体服务器的关键技术设计

0.引言

本篇文章主要讲述了在项目实战中运用到的一些关键技术分析,其中包括了很多综合知识点,如Nginx,流媒体相关等,如果想深入了解一些内容,可以阅读前面的知识,能够帮助你,加深对本文的理解,文章内容列表如下:

聊聊CDN系统的架构设计(1)

聊聊CDN技术与流媒体相关协议

详解Nginx系列

Nginx架构与Handler模块最详分析(2)

Nginx架构与Handler模块最详分析(3)

Nginx架构与Handler模块最详分析(1)

Nginx 源码分析之 Filter 模块(1)

Nginx 源码分析之 Upstream 模块(1)

超详细手把手搭建在ubuntu系统的FFmpeg环境

高并发网络 IO模型那些事

SRS流媒体服务器集群之Edge模式(2)

SRS流媒体服务器集群之Edge模式(1)

SRS流媒体服务器集群之Edge模式(3)

本篇文章是接着上篇CDN系统的架构设计继续讲解。流媒体服务器的性能可以从抗抖动能力、拥塞率和卡顿率等方面进行分析。当用户访问的频率过高或者并发的数量超过流媒体服务器所能承受的范围时,必须考虑通过限流来保证接口的可用性,从而防止出现因请求数量过多、压力过大造成服务器瘫痪等现象。因此,怎样提高流媒体服务器的抗抖动能力,平滑发送流量,降低卡顿率,减少丢包率是本文描述的重点。

1.流媒体服务器框架设计

前面的CDN的文章对一些相关算法进行了比较分析,对CDN流媒体的关键技术也进行了选取。本文就基于Nginx 服务器,结合 HLS 流媒体协议与令牌桶算法,针对流媒体服务器的数据处理模块、缓存模块发送模块进行设计。

302dde39d08f80729b7e9636c515e8e4.png

流媒体服务器的数据处理逻辑可分为以下几个部分:

(1) 数据处理模块:

数据处理模块主要负责以下几个功能:

(a) 接收来自客户端的请求和来自上级服务器的发送数据。

(b) 负责数据代理转发,在本地没有直播缓存数据时,需要向上级 CDN 或者源站发送请求从而获取数据,再把数据转发给客户端。

(c) 将录制频道的直播流文件写入存储系统,支持回看功能

(2) 缓存模块(Cache 模块):Cache模块用来从磁盘或者内存中读取实体文件,解析出文件码率,分包后对数据包进行时戳标记,并将标记好时戳的数据包压入数据发送模块。

(3) 发送模块(Shaper 模块):Shaper 模块的主要功能是将 Cache 模块输入的数据发送至客户端。Shaper 模块在进行数据处理时会根据当前的发送状态控制发送速率。Shaper 模块中的队列提供 FIFO(先进先出)的数据重传功能,保证数据在重传时不会乱序。流媒体服务器框架设计结构图如下所示:

83055a0fd9b9aac5650c84c03286b8fa.png

数据处理模块在收到来自客户端的请求后,会根据请求内容从 Cache 模块中寻找相应资源,如果 Cache 模块有相应的资源,则直接将数据交由 Shaper 模块进行发送处理;如果 Cache 模块中没有相应资源,则会主动到本地磁盘进行资源查找;如果本地磁盘没有相应资源,则会通过数据处理模块向源站发送请求信息,数据处理模块接收源站的相应数据并将数据存放在存储服务器中最终 Shaper 模块负责将用户所请求的内容返回给客户端。有时,对于一些热门的视频业务,数据处理模块会提前向源站请求资源存入存储服务器中,当用户请求到来时可以及时作出响应

2.流媒体服务器数据处理模块设计

2.1 数据接收和转发模块设计与实现

在处理多客户端请求时,可以在服务器端创建多个进程,但这种做法显然有一些弊端,创建进程本身是一个消耗计算机资源的过程,一味地创建进程也无法应对快速发展的业务量。I/O 多路复用技术让一个进程可以处理多个客户端需求,使得上述问题得以解决。这里也会涉及到很多优化,所以我们需要考虑到可以使用更优秀的商用框架。

I/O 多路复用技术可以理解为通过一种机制来监视多个文件描述符(文件描述符是计算机术语,用来表示指向文件的引用),当某个描述符就绪,对程序进行相应通知来完成读写操作。在 I/O 多路复用的解决方案中,有 select、poll 和 epoll 三种方式,这三种方式的本质都是同步 I/O。当然也可以做到异步。

epoll 模型不仅会告知应用程序有 I/O 时间到来,还会告知 I/O 流的相关信息,因此,应用程序不必遍历整个文件描述符集合就能定位到 I/O 事件,其时间复杂度为 O(1)。在性能上,select 模型和 poll 模型都会随着连接数的增加性能急剧下降,而 epoll 模型在处理成千上万的并发时,性能没有很大的改变,且其对连接数上限没有限制。epoll 模型最大的优点在于它只关心活跃的连接,而不关心总连接数,因此在实际的网络环境中,epoll 的效率远超于 select 模型与 poll 模型。epoll 除了在开发复杂度上略有难度,在内部使用数据结构、内核空间以及内在处理文件描述符机制等方面都优于 select,最重要的是,epoll 是实现了高并发的基础。select、poll 与 epoll比较如下图所示:

a35a6dc0c152b7dcb13b6085763a5300.png

Nginx 已有的数据处理模块就是基于 epoll 技术实现的,采用回调机制(callback)实现所有接收数据相关的函数,数据处理模块结构图如下所示:

0357d7f40ef9c8c8ac561e24eda1a7ef.png

代理转发模式下,nginx_http_upstream_init_request 函数作为该模式的入口函数,将此函数注册nginx_http_upstream_process 中,当进入调用链时,将需要监控的 epoll 句柄加入到nginx_add_event 中,在和上级源站建立连接成功后或者有数据交互时,epoll 监控到的句柄被触发,从建立连接,发送请求,接收数据(此过程称为代理回源)。

数据接收流程中,ngx_event_accept函数作为该模式的入口函数,在系统启动时,通过注册nginx_event_process_init 函数中,将需要监控的 epoll 句柄加入到 nginx_add_event中。当有客户端请求时,epoll 监控的句柄被触发,进而对连接请求进行处理

2.2 数据录制模块设计与实现

Nginx 目前没有提供支持 HLS 或者 TS 协议的录制模块,本文采用 epoll 异步通知机制,设计录制模块,数据录制模块异步线程调用关系图如下所示:

6411513b00947ef725a109657c19a9dd.png

上图中,Proxy 线程与文件 I/O 线程都是 Nginx 自带的模块。Proxy 线程负责接收来自于源站的请求数据,它将每个请求的文件描述符 fd 句柄加载到 epoll 中,再分发到各个录制线程中,同时通过调用 submit_task()接口把录制任务传递到录制线程中。录制线程则是把接收到的录制任务数据进行整合(包含对录制任务数据进行拼装、去重、校验、容错等步骤)后,同样将对应的文件描述符 fd 句柄加载到 epoll 中为 I/O 线程进行调用,同时调用 submit_task()接口进行异步写盘。在进行异步写盘时,默认写盘数据大小一般设置 1MB 作为基本单位,这是因为当写盘数据大小高于或是低于 1MB 时,内部存储器调用 read()函数次数过多,会导致 CPU过高,且数据排列组合多,浪费时间。因此选择 1MB 作为基本单位进行数据写盘时效率最高。文件 I/O 线程负责接收录制线程发送来的数据,是 Nginx 已封装好的模块,可以直接调用。

2.3 流媒体服务器数据缓存模块设计

在传统的 Nginx 流媒体服务器中,只有直播业务会采用缓存机制(Cache),但对于点播业务来讲,传统的 Nginx 流媒体服务器没有 Cache 机制。点播业务会采用直接读取磁盘上数据文件的方式,这种方式会导致磁盘 I/O 读写频繁,不仅影响系统效率,还会加速磁盘老化。因此,本节基于 Nginx 流媒体服务器,在点播业务上新增缓存模块,流媒体服务器会先从 Cache模块中获取所需要的数据,如果 Cache 模块中没有找到,才会从本地磁盘进行读取,这大大减少了磁盘的读写,从而减少系统开销,使性能得以优化与提升。

(1)Cache 模块模型架构

Cache 模块包含了 Cache 模块与本地磁盘的交互方式。当客户端发起请求后,数据接收模块会根据请求内容在 Cache 模块中寻找对应文件内容。Cache模块中的 Cache_read()函数和 Cache_open()函数分别负责对内容资源的读取与打开。请求来临时,服务器会根据用户请求所对应的文件描述符 fd 调用 Cache_read()函数进行内容读取,之后调用 Cache_open()函数打开文件。若此时从 Cache 模块中获取到了相应资源,则直接将资源交由 Shaper 模块进行发送处理;若没有从 Cache 模块中获取到相应资源,则会根据文件描述符 fd 从本地磁盘中进行查找

(2)本地磁盘包含两大模块,分别为 IO_open 模块和 IO_read 模块,其功能与 Cache 模块中的 Cache_read()函数和 Cache_open()函数类似,负责对内容资源的读取与打开,但调用方向有所区别

当在 Cache 模块中没有获取到索要找寻的数据时,会先调用本地磁盘的 IO_open 模块将文件打开,并将文件描述符 fd 返回给 Cache 模块中的 Cache_open()函数,之后调用 IO_read模块进行文件内容读取,并将读取的内容返回给 Cache 模块中的 Cache_read()函数。这样一来,Cache 模块中的数据会得到及时更新,当请求相同的资源时会直接从 Cache 模块中进行读取,从而减少了对本地磁盘的操作。对于长时间没有被请求的内容 Cache 模块会对其定期清理。Cache 模块模型架构图如下所示:

887e3c69fa3c2cc93b2cbdb80a27ec2d.png

Cache 模块从功能层面来讲可以分为两大部分

(3) Cache_block:用于存储文件数据块的内容和相关信息。

(4) Cache_file:是应用层进行文件操作的基本单元,使用文件句柄 Cache_fd 作为基本凭证。Cache_file 包括两种 block,分别为 cache_block_list 和 uncached_block_list,如下图所示:

d5ef46556c0cb36ca189b81d43ca53b7.png

2.4 Cache 模块的主要接口设计

Cache 模块的主要接口设计如下:

(5) Cache_open()

文件打开接口,主要功能如下:

(a) 查看文件状态

(b) 在文件 hash 表中查找文件是否已经被打开,如果已经被打开,则刷新引用计数(ref),并直接返回;如果未被打开,则在 hash 表里增加一个表项,并使用 dump_file()接口打开该文件。

(6) Cache_read()

读文件接口,主要功能如下:

(a) 增加文件引用数。

(b) 在 cache_file 的 block_list 中查找块是否存在,如果存在则直接返回给调用者;如果未找到,则向空闲块列表中申请一个新的块。

(c) 调用 dump_file()接口进行文件读取

(d) 将块加入到 used_list 和 cache_file 中。

(3) File_closed()

文件关闭接口,主要功能如下:

(a) 减少引用计数,一旦引用计数为 0,则执行文件关闭流程。

(b) 同步调用 dump_file()接口来关闭文件。

(c) 同步和异步销毁 cache_file 文件,其中需要清空 cache_file 文件所拥有的 block_list。

(4) Io_read() 文件预读接口,主要功能如下:

(a) 检查 block 是否已经在 cache 中存在,如果存在则无需进行预读。

(b) 向预读线程提交预读任务,预读从线程中增加文件的引用计数,防止预读过程中,文件被关闭。

(c) 调用 cache_read()接口,同时回滚文件的引用计数。

(5) Block_aging()

块老化接口,主要功能如下:

(a) 遍历 used_list,将引用数为 0 的 block 块加入老化列表,并按照时间排序。

(b) 遍历老化列表,将排名靠前的 N 个块从 used_list 中摘除,放入 free_list,一旦空闲块数量达到要求(阈值的两倍),则停止老化。

2.5 Cache 模块的功能设计与实现

Cache_file 用来存储一个个的缓存数据块,数据块包括两种,cache_block_list 和 uncached_block_list。在对这两个模块进行数据存储时,可供选择的数据结构有链表、二叉树、哈希结构

(1) 链表(含单向链表、双向链表)链表的特点是便于元素的增加与删除,开销较小,但在对元素的查找时效率很低。因此其多用在数据量较小,且不需要排序的场景。

(2) 二叉树(含红黑树、平衡二叉树)

二叉树的特点是在插入任意元素时都可以自动进行排序,使元素保持有一定的特性。由于二叉树的空间占用与输入的数据保持一致,所以不需要为二叉树预先分配固定的空间。因此,二叉树多是用在频繁对数据进行增删和查询操作的场景下,在增删改查的同时需要时刻保证数据的有序排列,且无需预先知道数据量的大小。二叉树的时间复杂度是 O(log(n))

(3) 哈希(hash) 哈希结构使用链表和数组来管理元素,在理想的哈希算法的支持下可以达到接近数组的随机访问效率,因此其使用场景为无需保证元素的有序性且频繁对数据进行操作的场景。流媒体服务器应用在多用户直播或点播场景中,此场景的数据特点是数据量大且保存媒体文件时无需排序。当一个用户请求某一路直播(比如 CCTV2),哈希结构可以快速根据 CCTV2 的 hash 值很快找到对应的缓存块,从中读取数据,时间复杂度可以近似达到 O(1)。若使用链表或二叉树,需对缓存中的每项内容进行遍历,时间复杂度较高。其次,媒体文件本身并无排序需求,不符合二叉树的使用场景。另外,哈希结构的代码开发简便且可维护性较强。综上选择使用哈希数据结构对流媒体服务器的 Cache 模块进行设计开发。

在数据量较大时,哈希结构会产生冲突。解决哈希冲突采用的方案有两种:第一种是线性探索,相当于在冲突之后建立一个单链表,这种情况下,插入和查找以及删除操作消耗的时间会达到 O(n);第二种方法是开放寻址,这种方法不需要更多的空间,在最坏的情况下,时间复杂度也只会达到 O(n)

Cache_file 的哈希结构在 Cache 模块中的实现方式。哈希表的 key 值是由点播或者直播的媒体文件名通过哈希表达式计算得来。每一个 file 文件后又以链表的形式存有若干block,当需要查找资源时,只需按照哈希表达式来进行查找即可,这样的数据查找方式在一定程度上提高了查找效率。Cache_file 的哈希实现方式如下所示:

4561f4e26c1cc0e656b643788b775427.png

(4) 在读取数据入队之前,Cache 模块会将数据打上时间戳,时间戳的计算方法模拟令牌桶原理,每一个数据包的时间戳计算公式如下:

09759e16851f298e7f2ca397d19ef216.png

T 当前数据包的 RTC 时间戳size 为当前数据包的大小,TS 格式下的媒体元数据单位是 1316 字节,speed 为当前流媒体文件码率t上一个数据包的 RTC 时间戳。从公式可以看出,计算单位时间 1 秒内发送的报文数 n,可利用如下公式:

ac9c6ff9f90e3f95c167d2ed2fdc018d.png

即给 n 个数据包打上时间戳。即在 time 秒内理论发送数据可以达到如下公式:

5a2abf77f071c1c755bdf06bfcf274be.png

从上面的步骤可以看出,通过给数据包打 RTC 时戳的方式,可以计算单位时间内的发包个数,从而对其进行精确控制,实现均匀控制码率的效果,与传统令牌桶的实现方式相比,这种时间戳控制方式,在保证速率控制的同时,减少了发送模块对令牌结构出队入队(保证一定的数据包个数),申请内存等操作,提升了系统性能。

3.发送模块设计

Shaper 模块即数据发送模块,主要负责将 Cache 模块入队的数据进行发送,并计算发送结果,根据单位时间内发送的字节数 B 计算出当前的链路带宽 b,实时更新每一个发送对端的链路速度,针对每一路用户的数据流进行数据降速或者升速。上面和下面这些公式就说明怎样去控制发送速率,非常重要。

3.1Shaper 模块模型架构

流媒体服务器发送模块的结构如下图所示,图中说明了数据的流程。

43648cfd21d18c321c5c330d9e7dad82.png

(1)本系统结合具体的业务场景增加了桶水位线的优化:如果当前媒体文件的码率为 s,流媒体服务器数据缓存模块以每 1/s 的精度,从当前 RTC 时钟自加,给每个数据包作时戳标记。数据发送模块预先开辟大小为 m 的队列,将做好时间戳标记且需要发送出去的数据入队,当数据包到达发送时间后,获得令牌,出队发送,每个队列有一条上水位线 H 和下水位线 L,当网络阻塞时,若队列内的数据大于等于 H,则新到的数据不入队,队列中的每个数据时戳向后移动 d 个精度,这样降低了发送速率减少了网络拥塞降低后的速率为 如下计算公式:

7c3e6a58bfdd4413b3ff8679de691b6a.png

网络状况良好,无发送失败记录,并且队列中的数据量[L,H]之间,则将令牌生成间隔相对提高%d,按缓慢升速发送,如下计算公式:

4639c9af422d874b76c43731f963c086.png

直到队列水位线下降到 L 处为了保留缓存,数据发送速率恢复到媒体文件码率 s,当数据发送成功后,该数据的令牌被销毁。所以令牌桶的原理就是通过控制令牌产生的速度,将数据发送码率平稳在 s 左右,对于每一个连接,令牌桶的数据流量为 B,计算公式如下:

b962e4301be4de101aee9f2fcd08822a.png
4091b4d733f55be60225bd63a141f6d3.png

3.2 Shaper 模块运行机制

Shaper 模块设计采用异步多线程运行机制,应用添加线程包中的数据来自于本地磁盘或是 Cache 模块中的数据文件。Shaper 模块中创建若干个发包线程,这些线程采用异步方式可以对应用添加包中的数据同时进行操作,这样做的好处就是,当多个请求发来且访问同一资源时,无需等待资源访问结束,在空闲等待时同时可以访问其他资源,提高了服务器的发送效率。如下图所示:

2a6085a334634d7c9cd710a1df4d371d.png

3.3 令牌桶在 Shaper 模块中的应用

将具体叙述如何将令牌桶算法应用在 Shaper 模块中以及应用令牌桶算法对流量整形产生的影响。在服务器的内部存储池中设计令牌产生模块,实现以一定速率产生令牌。这些令牌在代码中的具体实现是一些虚拟的数据包,当流媒体服务器接收数据时,会根据系统设定的匹配规则对接收的数据进行分类,符合规则的数据交由令牌桶处理,而不符合规则的数据包,直接拒之门外。Shaper 模块的数据来源有两处,一处是来自 Cache 模块,一处是来自本地磁盘。不论数据来自哪里,Shaper 模块都会将收到的数据打包并作时戳标记之后放入队列。数据包根据时戳会决定何时出队发送,出队后的数据包会在内部存储池中求取令牌,只有拿到令牌的数据包才可发送给客户端。当数据包没有获得令牌(通常是令牌桶中的令牌不足的原因造成),服务器为队列设置阻塞模式,数据包无法出队,阻塞在队列中。若阻塞数据包超过了队列的上线,则新的数据包无法入队。因此,我们可以通过控制令牌产生速率来控制数据包的出队入队速率,从而达到对流量的控制。令牌桶在 Shaper 模块中的应用如下图所示:

c3917af95c695861b01daa35b7b206bc.png

假设令牌桶的容量为 D(单位:字节),产生令牌的速率为 v(单位:字节/秒),输出数据的最大速率为 V(单位:字节/秒),突发的时间为 s(单位:秒)。当突发数据来临前假设桶中的容量为 D-C,则有:

b9dad2382734a92140b8000c9bb83c87.png

经过变型,则有:

57147d3e325b36d22b8cb8357e84d30d.png

由此可以计算出所能承受的突发时间,从而可以根据系统突发时间上限在代码中进行设置。

令牌桶实现 Shaper 模块的主要接口如下:

(1) bool Udp Shaper Channel()

此函数实现的功能是以 UDP 方式发送媒体数据,首先根据时间戳下发 ret 数据,之后发送媒体数据,如果成功则返回 True,失败则返回 False。

(2) void Interleaved Shaper Channel()

此函数首先发送信令(发送信令的时戳为当前时间),再根据时戳决定当前是否要发送媒体数据。若到达发送媒体数据的时间则进行发送;若没有到达,则等待。由于 TCP 有拥塞控制,当发送失败时,数据包的时戳时间会改为当前时间加上延迟时间

(3) void Download Shaper Channel()

此函数功能为下载媒体数据,负责发送码率的升速与降速。在函数中设置发送码率的上线发送码率,同级周期内(1s)若发送失败率为 0,则提速 5%;若发送失败率小于 10%,则降速 5%。设置升速时,升速不能超过发送码率的上线,降速不能低于 12Kbps。

3.4 数据重传

Shaper 模块在发送数据包的过程中极有可能发生包丢失等异常情况,本系统在 Shaper 模块中增加重传逻辑用以应对此类情况。设计逻辑图如下图所示:

1dc077444e2ed4be3c35224a41f3cda0.png

Shaper 模块在进行数据传输时,会根据 send()函数返回值对数据是否有丢包进行判断。例如,在传输 10 个数据包的情况下,当 send()返回值为 7 * 1316 字节(1316字节为 TS 格式下的媒体元数据单位),即说明有 3 个数据包在传输时发生丢失,此时根据时戳将待传输的数据包加入重传队列,在下次传输数据时优先传输重传队列中的数据包,之后再对 Cache 模块中的新数据队列进行传输。具体重传时间的计算公式如下:

94b6dcd10b1b66b2c8c82521da4b639a.png

4 流媒体服务器参数优化

Nginx 服务器的配置系统很灵活。只需通过对配置项的增加、修改即可实现第三方模块的信息提供。将对 Nginx 服务器与内核的配置文件进行相关设置,从而实现流媒体服务器的参数优化。

4.1 流媒体服务器配置文件优化

为了提高 Nginx 服务器的性能,需对流媒体服务器进行参数调优。Nginx 可以通过修改默认安装路径下/usr/local/nginx/conf/nginx.conf 的 nginx.conf 文件来进行参数配置。调优模块分为三类,全局块、Events 块和 HTTP 块

(1)全局块,配置文件优化如下:

worker Process_num:4; worker_cpu_affinity 0001 0010 0100 01000;

(a) worker Process_num 是 Nginx 创建的 worker 进程的个数。Nginx 中基本的网络事件都由 worker 进程处理,所以 worker 进程的数量对 Nginx 的性能会有很大的影响。如果设置太小,则 Nginx 服务器很难应对高并发的请求数量,浪费了资源;如果设置过多会 导 致 进 程 之 间 的 频 繁 切 换 , 造成了不必要的开销 。一 般来说 ,通常将worker Process_num 设置为服务器CPU的内核数量。

(b) worker_cpu_affinity 是为每个 work 进程设置一个固定 CPU 内核。当 Nginx 中的每个worker 进程都处于工作状态时,可能产生多个 worker 进程争抢一个内核的现象,设置 worker_cpu_affinity 可以减少因选择不同 CPU 内核产生的开销。

(2)Events 块,配置文件优化如下:

worker_connections:102400; use epoll; 

Events 块对 Nginx 中事件模块进行配置来控制和处理访问连接。

(a) worker_connections 用来设置每个 worker 进程的最大连接数,最大连接数应与全局块中的 worker File_limit Num 值一样。

(b) use 用来设置事件驱动模型。在不同操作系统下选取的事件驱动模型也有所不同,对于 Linux 系统来说,epoll 模型是较为高效的事件管理机制,在性能方面相对于标准事件模型 select 和 poll 要高很多。

(3)HTTP 块主要从网络连接设置、文件缓存设置、Socket 优化设置三部分组成。

HTTP 块是 Nginx 配置的核心部分,对连接处理行为的设置很多都需要在此块中进行设置,其中包括负责负载均衡 upstream 块,对虚拟主机进行配置 server 块,以及反向代理location 块等。网络连接设置,配置文件优化如下:

(a) 网络连接超时设置通过设置连接超时时间上限,可以在请求连接超时后,对其进行关闭,将超时连接所占用的系统资源进行释放。在面对高并发数量请求时,可以提供更好的资源利用率和服务效率。

tcp_nodelay on; SO_REUSEPORT 

(4)文件缓存设置,配置文件优化如下:

sendfile 指令指定 Nginx 是否调用 Linux 的系统函数 sendfile()来发送文件。如果服务器用来进行下载等对磁盘 I/O 消耗高的操作,可以设置为 off;如果对于普通应用必须设定为 on,sendfile()函数用以提高文件的发送效率

open_file_cache 指令用于开启文件缓存功能。 max 为缓存的数量上限,应和worker_connections数量保持一致,inactive 设定一个时间,当文件在此时间内没被请求,则系统会删除缓存。

open_file_cache_valid 用来指定检查缓存有效性的时间间隔。

open_file_cache_min_uses 表示 inactive 时间内文件的最小使用次数,如果小于此值,文件将被删除。

send_file on; open_file_cache max = 1024 inactive = 30s; open_file_cache_valid 60s; open_file_cache_min_uses 3; 

(5)Socket 优化设置,配置文件优化如下:

tcp_nodelay 指令开启后,服务器将不会对数据进行缓存reuseport指令用来设置共享 socket。

reuseport 的设置是用来解决 Nginx 中“惊群”问题。“惊群”问题是指一个连接来临时会唤醒多个进程对其进行抢夺处理。

在连接来临时,多个 worker 线程共享一个套接字,这时便会出现多个线程去抢占资源的情况。传统的 Nginx 在处理这个问题时有两种解决方式:一是配置 accept_mutex on,在 accept()函数调用之前采用锁机制,获得锁的进程才有权对套接字进行处理,但锁机制会影响服务器的性能,另外,这种方式的弊端是所有的 worker 进程都会被唤醒去争夺锁,会对内核资源造成浪费;二是设置 accept_mutex off,这种处理方式虽然少了锁机制,提高了系统响应能力,但会出现“惊群”问题,在 worker 进程数增多时会对系统带来一定性能影响。

tcp_nodelay on; SO_REUSEPORT 

注意:以上这些配置文件仅供参考,根据实际情况去优化。

(6)reuseport 允许多个套接字监听同一端口,图中 80 端口为 HTTP 默认端口,内核能够在套接字中对传入的连接进行负载均衡自动选择 worker 进程进行处理,而不必唤醒所有 worker 进程。采用此种方式进行 SO_REUSEPORT 配置时,需将 accept_mutex 关闭。配置如下图所示:

b254bbd9d810cd923b75645af552221f.png

未 开 启SO_REUSEPORT 配置时,请求来临时,由一个套接字 socket 进行绑定和监听并将连接交给各个进程处理;当开启了 SO_REUSEPORT 配置时,多个进程可以同时绑定和监听同一个TCP/UDP 端口,请求交由哪个进程处理由内核决定,实现了在内核层面的负载均衡。未开启和开启SO_REUSEPORT对比图如下:

0b565d00dae171f8a386f6e316996b11.png

5.Linux 内核参数优化

默认的 Linux 内核参数无法满足高并发访问量情况对服务器系统的要求,为了满足特定场景的使用需要同时对 Linux 内核参数进行设置,而且当 Nginx 服务器用于不同目的时,对于 Linux 参数的要求都会有很大的不同。针对以上情况,本文介绍对 Linux 内核参数的优化,使其能够提高 Nginx 对并发请求的服务性能。

Linux 内核参数选项是通过修改/etc 目录下的 sysctl.conf 文件来更改

(1) fs.file--max = 999999 此参数用来限制一个进程可以同时建立的最大连接数。Nginx 服务器通过每个 worker 进程来进行实际的请求响应,所以此参数的设置会影响到 Nginx 的最大并发连接数量。

(2) net.ipv4.tcp_tw_reuse = 1 此参数用来开启重用功能,可以设置为 1 或 0。当设置为 1 时,服务器上处于 TIME_WAIT状态的请求连接会被系统重新建立 TCP 连接。

(3) net.ipv4.tcp_fin_timeout = 10 这个参数是用来设置保持在 FIN_WAIT_2 状态的时间。在一个 TCP 连接关闭过程中,套接字会有一个 FIN_WAIT2 状态到 TIME_WAIT 状态的变化,将此参数设置为 10,可以使套接字更快的进入 TIME_WAIT 状态

6.总结

本文主要介绍了在CDN与流媒体服务器合作下,当流媒体数据存储在服务器上,是如何处理,主要划分为三大模块,分别为数据处理模块、数据缓存模块、数据发送模块,分别进行了模块的架构设计、模块的主要功能的讲解,特别在数据发送模块中设计了令牌桶算法(这个很重要),对于流量控制很有帮助。欢迎关注,收藏,转发,分享。

后期关于项目知识,也会更新在微信公众号“记录世界 from antonio”,欢迎关注

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SRS定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。SRS提供了丰富的接入方案将RTMP接入SRS,包括推送RTMP到SRS、推送RTSP/UDP/FLV到SRS、拉取SRSSRS还支持将接入的RTMP进行各种变换,譬如将RTMP转码、转发给其他服务器、转封装成HTTP-FLV、转封装成HLS、转封装成HDS、录制成FLV。SRS包含支大规模集群如CDN业务的关键特性,譬如RTMP多级集群、VHOST虚拟服务器、无中断服务Reload、HTTP-FLV集群。此外,SRS还提供丰富的应用接口,包括HTTP回调、安全策略Security、HTTP API接口、RTMP测速。SRS在源站和CDN集群中都得到了广泛的应用Applications。 最新版本SRS 2.0-258 ,使用cygwin 在windows下的编译 。 修改代码 setrlimit (2048) ,使其默认能支持 2048个连接 在i7笔记本上 ,测试 500路视频转发 ,srs.exe进程 占用cpu 5%,出奇的低。 以下是 编译错误笔记和修改的配置项。 *.修改 Makefile CXXFLAGS = -ansi -Wall -g -O0 -fPIC -std=gnu++11 -D__GLIBC__=2 -D__GLIBC_MINOR__=31 -DFD_SETSIZE=2048 1.depends.sh what a fuck, os not supported. ingore it !!!! 2. make[1]: 进入目录“/mnt/srs-2.0-r5/trunk/objs/st-1.9” if [ ! -d LINUX_3.0.4(0.338/5/3)_DBG ]; then mkdir LINUX_3.0.4(0.338/5/3)_DBG; f i /bin/sh: -c:行0: 未预期的符号 `(' 附近有语法错误 /bin/sh: -c:行0: `if [ ! -d LINUX_3.0.4(0.338/5/3)_DBG ]; then mkdir LINUX_3.0.4 (0.338/5/3)_DBG; fi' make[1]: *** [Makefile:348:LINUX_3.0.4(0.338/5/3)_DBG] 错误 1 make[1]: 离开目录“/mnt/srs-2.0-r5/trunk/objs/st-1.9” make: *** [Makefile:441:linux-debug] 错误 2 build st-1.9 failed, ret=2 3.depends.sh make cygwin-debug 提示 static lib failed. # st-1.9 # Some platforms allow to define FD_SETSIZE (if select() is used), e.g.: DEFINES += -DFD_SETSIZE=4096 ##################################################################################### if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then # check the cross build flag file, if flag changed, need to rebuild the st. _ST_MAKE=linux-debug && _ST_EXTRA_CFLAGS="-DMD_HAVE_EPOLL -DMALLOC_STACK" 4.install CherryPy-3.2.4" require sudoer failed 解压 CherryPy-3.2.4.zip 至 objs目录 4.1 local_ip.sh 改成 ip='0.0.0.0' #ip=`ifconfig|grep "inet "|grep -v "127.0.0.1"|awk -F 'inet ' 'NR==1 {print $2}'|awk '{print $1}'|sed "s/addr://g"` 5. libst.def st_get_eventsys_name @112 st_set_eventsys @113 ibssl.a objs/openssl/lib/libcrypto.a -ldl /usr/lib/gcc/i686-pc-cygwin/7.4.0/../../../../i686-pc-cygwin/bin/ld: objs/src/ap p/srs_app_st.o: in function `Z11srs_st_initv': /mnt/srs-2.0-r5/trunk/src/app/srs_app_st.cpp:217: undefined reference to `st_set _eventsys' /usr/lib/gcc/i686-pc-cygwin/7.4.0/../../../../i686-pc-cygwin/bin/ld: /mnt/srs-2. 0-r5/trunk/src/app/srs_app_st.cpp:219: undefined reference to `st_get_eventsys_n ame' /usr/lib/gcc/i686-pc-cygwin/7.4.0/../../../../i686-pc-cygwin/bin/ld: /mnt/srs-2. 0-r5/trunk/src/app/srs_app_st.cpp:222: undefined reference to `st_get_eventsys_n ame' /usr/lib/gcc/i686-pc-cygwin/7.4.0/../../../../i686-pc-cygwin/bin/ld: /mnt/srs-2. 0-r5/trunk/src/app/srs_app_st.cpp:229: undefined reference to `st_get_eventsys_n ame' collect2: 错误:ld 返回 1 make[2]: *** [objs/Makefile:302:objs/srs] 错误 1 make[2]: 离开目录“/mnt/srs-2.0-r5/trunk” make[1]: *** [Makefile:38:server] 错误 2 make[1]: 离开目录“/mnt/srs-2.0-r5/trunk” make: *** [Makefile:8:default] 错误 2 6. ulimit -n 2048 srs_app_config.cpp 增加代码修改 进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误 rlimit l; getrlimit(RLIMIT_NOFILE,&l); l.rlim_cur = get_max_connections() + 100; setrlimit(RLIMIT_NOFILE, &l); srs_info("setrlimit => soft:%d , hard:%d\n",(int)l.rlim_cur,(int)l.rlim_max);
SRS(Simple Rtmp Server)的定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。 • 运营级: 商业运营追求极高的稳定性,良好的系统对接,以及错误排查和处理机制。譬如日志文件格式,reload,系统HTTP接口,提供init.d脚本,转发,转码,边缘回多源站,都是根据CDN运营经验作为判断这些功能作为核心的依据。 • 互联网: 互联网最大的特征是变化,唯一不变的就是不断变化的客户要求,唯一不变的是基础结构的概念完整性和简洁性。互联网还意味着参与性,听取用户的需求和变更,持续改进和维护。 • 直播服务器: 直播和点播这两种截然不同的业务类型,导致架构和目标完全不一致,从运营的设备组,应对的挑战都完全不同。两种都支持只能说明没有重心,或者低估了代价。 • 集群: FMS(AMS)的集群还是很不错的,虽然在运营容错很差。SRS(Simple Rtmp Server)支持完善的直播集群,Vhost分为源站和边缘,容错支持多源站切换、测速、可追溯日志等。 • 概念完整性: 虽然代码甚至结构都在变化,但是结构的概念完整性是一直追求的目标。从SRS(Simple Rtmp Server)服务器,P2P,ARM监控产业,MIPS路由器,服务器监控管理,ARM智能手机,SRS(Simple Rtmp Server)的规模不再是一个服务器而已。 • 简单实现: 对于过于复杂的实现,宁可不加入这个功能,也不牺牲前面提到的要求。对于已经实现的功能的代码,总会在一个版本release前给予充分的时间来找出最简答案。不求最高性能,最优雅,最牛逼,但求最简单易懂。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值