京东云罗玉杰:OpenResty 在直播场景中的应用


2019?年?3?月?23?日,OpenResty?社区联合又拍云,举办?OpenResty?×?Open?Talk?全国巡回沙龙·北京站,京东云技术专家罗玉杰在活动上做了《?OpenResty?在直播场景中的应用?》的分享。
OpenResty?x?Open?Talk?全国巡回沙龙是由?OpenResty?社区、又拍云发起,邀请业内资深的?OpenResty?技术专家,分享?OpenResty?实战经验,增进?OpenResty?使用者的交流与学习,推动?OpenResty?开源项目的发展。活动已经在深圳、北京两地举办,未来将陆续在武汉、上海、杭州、成都等城市巡回举办。

罗玉杰,京东云技术专家,10?余年?CDN、流媒体行业从业经验,热衷于开源软件的开发与研究,对?OpenResty、Nginx?模块开发有较深入的研究,熟悉?CDN?架构和主流流媒体协议。
以下是分享全文:
大家下午好,我是来自京东云的罗玉杰,今天给大家分享的主题是?《OpenResty?在直播场景中的应用》。
项目需求
京东云前期的服务是基于?Nginx?二次开发的,之后因为要对接上云的需求,于是新做了两个服务,一个是对接云存储的上传服务,另一个是偏业务层的直播时移回看服务。项目的需求是做视频数据上云,主要是视频的相关数据对接云存储,需求的开发周期很紧,基本上是以周为单位。
我们之前的服务用?C?、C++?开发,但?C?和?C++?的开发周期很长。我们发现这个项目基于?OpenResty?开发是非常适合的,可以极大地缩短开发周期,同时提高运行效率,并且?OpenResty?对运维非常友好,能提供很多的配置项,让运维根据线上动态修改一些配置,甚至运维都可以看懂代码的主流程。
项目体系结构

上图是一个直播服务的主流体系结构,先是主播基于?RTMP?协议推到?CDN?边缘,接着到视频源站接入层,然后把?RTMP?流推送到切片上传服务器,上面有两个服务:一个是切片服务,把流式的视频流进行切片存储到本地,生成?TS?视频文件和?M3U8?文本文件,每形成一个小切片都会通知上传服务,后者将这些?TS?文件和?M3U8?文件基于?AWS?S3?协议上传到云存储服务,我们的云存储兼容?AWS?S3?协议。在此基础上,我们用?OpenResty?做了一个直播时移回看服务,用户基于?HLS?协议看视频,请求参数里带上时间段信息,比如几天之前或者几个小时之前的信息,此服务从云存储上下载?M3U8?信息进行裁剪,再返回给用户,用户就可以看到视频了。HLS?协议的应用面、支持面很广,各大厂商、终端支持得都非常好,而且对?HTTP?和?CDN?原有的技术栈、体系非常友好,可以充分地利用原来的一些积累。有的播放是基于?RTMP,HDL(HTTP?+?FLV)协议的,需要播放器的支持。
项目功能
1、基于?s3?PUT?协议将?TS?文件上传至云存储。
2、S3?multi?分片上传大文件,支持断点续传。这个服务重度依赖于?Redis,用?Redis?实现任务队列、存储任务元数据、点播?M3U8。
3、基于?Redis?实现任务队列的同时做了?Nginx?Worker?的负载调度。在此基础上做了对于后端服务的保护,连接和请求量控制,防止被短时间内特别大的突发流量把后端的云服务直接打垮。实现任务队列之后,对后端的链接数是固定的,而且请求处理看的是后端服务的能力,简单地说,它处理得多快就请求得多快。
4、为了保证云和服务的高可靠性,我们做了失败重试和异常处理、降低策略。其中,任务失败是不可避免的,现在也遇到了大量的任务失败,包括链接失败、后端服务异常等,需要把失败的任务进行重试,降级。把它在失败队列里面,进行一些指数退避。还有一些降级策略,我这个服务依赖于后面的?Redis?服务,和后端的云存储服务,如果它们失败之后,我们需要做一些功能的降级,保证我们的服务高可用。在后端?Redis?服务恢复的时候再把数据同步过去,保证数据不会丢失。
5、还有就是生成直播、点播?M38,为后续的服务提供一些基础数据。如直播时移回看服务。
AWS S3 协议
AWS?S3?比较复杂的就是鉴权,主要用它的两个协议,一个是?PUT,一个是?MULTI?PART。

AWS?S3?的鉴权和?Nginx?中的?Secure?Link?模块比较相似,将请求相关信息用私钥做一个散列,这个散列的内容会放到?HTTP?头?authorization?里面,服务端收到请求后,会有同样的方式和同样的私钥来计算这个内容,计算出的内容是相同的就会通过,不相同的话会认为是一个非法请求。


它主要分三步骤,第一步是创建任务,创建任务之后会返回一个?ID?当做任务的?Session?ID,用?POST?和?REST?规范实现的协议。初始化任务之后,可以传各种分片了,然后还是用?PUT?传小片,加上?Session?ID,每一片都是这样。

上传任务成功之后,会发一个?Complete?消息,然后文件就认为是成功了,成功之后就会合并成一个新的文件,对外生成一个可用的大文件。
HLS 协议
HLS?协议,全称是?HTTP?LIVE?STREAMING?协议,是由苹果推出的,可读性很强。里面的每一个片都是一个?HTTP?请求,整个文本协议就是一个索引。

上图是每一个视频段的时长,这个是?8?秒是视频的最大长度。直播的应用中会有一个?Sequence?从零开始递增的,如果有一个新片,就会把旧片去掉,把新的加上去,并增加?Sequence。
任务队列、均衡、流控
下面再介绍一下具体的功能实现,任务收到请求之后不是直接处理,而是异步处理的。先把请求分发到各个?Worker?的私有队列,分发算法是用的?crc32,因为?crc32?足够快、足够轻量,基于一个?key?视频流会有域名、App、stream,再加上?TS?的文件名称。这样分发可以很好地做一次负载均衡。基于这个任务队列,可以处理大量的突发请求,如果突然有了数倍的请求,可以把这些消息发到?Redis?里,由?Redis?存储这些请求。每个?Worker?会同步进行处理,把?TS?片上传,上传完之后再生成?M3U8?文件。我们现在对后端固定了连接数,一个?woker?一个链接,因为存储集群的连接数量是有限的,现在采取一个简单策略,后端能处理请求多快,就发送多快,处理完之后可以马上发送下一个。因任务队列是同步处理,是同步非阻塞的,不会发送超过后端的处理能力。
我们未来准备进行优化的方向就是把任务队列分成多个优先级,高优先级的先处理,低优先级的降级处理。比如我们线上遇到的一些视频流,它不太正常会大量的切小,比如正常视频?10?秒一片,而它?10?毫秒就一片,这样我们会把它的优先级降低,防止异常任务导致正常任务不能合理地处理。以后就是要实现可以动态调解链接数、请求速率和流量。如果后端的处理能力很强,可以动态增长一些链接数和请求速率,一旦遇到瓶颈后可以动态收缩。

任务分发比较简单,主要就是上面的三行代码,每一个?Worker?拿到一个任务后,把任务分发给相应的?Worker?,它的算法是拿到总?Worker?数然后基于?crc32?和?key?,得到正确的?Worker?ID,把它加到任务队列里。这样的做法好处是每个任务分发是非单点的,每一个?Worker?都在做分发,把请求的任务发到任务队列里,请求的元信息放入?Redis?里面,还有一个就是任务拉取消费的协程,拉取任务并执行。
失败重试、降级、高可靠
如果数据量大会有很多失败的任务,失败任务需要放入失败队列,进行指数退避重试。重试成功后再进行后续处理,比如添加进点播?m3u8、分片?complete。分片?complete?是如果原来有?100?个任务会同时执行,但是现在有?3?个失败了,我们可以判断一下它是不是最后一个,如果是最后一个的分片就要调一下?complete,然后完成这个分片,完成整个事务。
同时我们做了一个?Redis?失败时的方案,Redis?失败后需要把?Redis?的数据降级存到本地,一部分存到?share?dict,另一部分用?LRU?cache,TS?对应?m3u8?的索引信息会用?share?dict?做缓存。LRU?主要是存一些?m3u8?的?key,存储哪些信息和流做了降级,Redis?恢复后会把这些信息同步到?Redis。因为存在于各个?Worker?里面数据量会比较大,有些任务会重复执行,我们下一步工作就想基于?share?dict,加一个按照指定值来排序的功能,这样就可以优先处理最近的任务,将历史任务推后处理。
我们还有一些?M3U8?的列表数据存储在?Redis,因为线上的第一版本是单实例的,存储空间比较有限,但是现在对接的流量越来越多,单实例内存空间不足,于是我们做了支持?Redis?集群的工作,实现?Reids?高可用,突破内存限制。
还有一个比较兜底的策略:定期磁盘巡检,重新处理失败任务。事务可能是在任何的时点失败的,但是只要我们能够重做整个任务,业务流程就是完整的。
遇到的问题和优化方案
第一版的时候是全局的单一任务队列,基于?resty?lock?的锁取保护这个队列,每一个?woker?争用锁,获取任务,锁冲突比较严重,CPU?消耗也高,因为那个锁是轮询锁,优化后我们去掉了一个锁实现了无锁,每一个?Worker一个任务队列,?每个?Worker?基于?CRC_32?分发任务。
旧版一个?TS?更新一次?M3U8,一次生成一个哈希表,数量较多的情况下?CPU?开销比较大。我们进行了优化,做了一些定时触发的机制,进行定期更新,因为点播?M3U8?对时间是不敏感的,可以定期地更新,减少开销。当然直播的??还是实时生产的,因为要保证直播的实时性。
直播方面如果异常切片太多,用户也不能很好观看,会进行主动丢片,主要是基于?Redis?锁去实现;对于?Redis?内存消耗高的问题我们搭建了?Redis?集群。
直播时移回看服务
我们开发了一个直播时移回看服务,根据用户请求的时间去后台下载相应的?M3U8?的数据进行裁剪拼接返回给用户。这一块的?M3U8?信息不是很大,非常适合用?MLCACHE?保存,它是一个开源的两级缓存,Worker?一级的和共享内存一级,因为共享内存缓存有锁冲突,MLCACHE?会把一些热点数据缓存到?Worker?级别,这样是无锁的,使用后效果非常好,虽然文件不大,但是运行时间建连,网络IO耗时很大,经过缓存之后可以大大提高处理效率,节省时间。时移的时候每一个用户会也一个?Session?记录上次返回的?M3U8?位置,因为直播流会有中断,不是?24?小时都有流的,用户遇到了一个断洞,可以跳过看后面的视频,时移不需要等待,并且用户网络短暂异常时不会跳片。
OpenResty x Open Talk 北京站同期文章:
Polaristech?刘洋:基于?OpenResty/Kong?构建边缘计算平台
点击「阅读原文」观看演讲视频和 PPT~
快 来 找 又 小 拍

推?荐 阅 读
关于技术
我眼中的?Nginx(五):Nginx?—?子请求设计之道
我眼中的?Nginx(四):是什么让你的?Nginx?服务退出这么慢?
我眼中的?Nginx(二):HTTP/2?dynamic?table?size?update
?告 诉 大 家 你 “在 看” 这 篇 文 章???
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号随时掌握互联网精彩
- 1 习近平将发表二〇二六年新年贺词 7904141
- 2 2026年国补政策来了 7808738
- 3 东部战区:开火!开火!全部命中! 7712893
- 4 2026年这些民生政策将惠及百姓 7616985
- 5 小学食堂米线过期2.5小时被罚5万 7519709
- 6 解放军喊话驱离台军 原声曝光 7428214
- 7 为博流量直播踩烈士陵墓?绝不姑息 7327605
- 8 每月最高800元!多地发放养老消费券 7238391
- 9 数字人民币升级 1月1日起将计付利息 7141831
- 10 2026年1月1日起 一批新规将施行 7040675








又拍云
