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

栏目: 服务器 · Nginx · 发布时间: 6年前

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

2019 年 3 月 23 日,OpenResty 社区联合又拍云,举办 OpenResty × Open Talk 全国巡回沙龙·北京站,京东云技术专家罗玉杰在活动上做了《 OpenResty 在直播场景中的应用 》的分享。

OpenResty x Open Talk 全国巡回沙龙是由 OpenResty 社区、又拍云发起,邀请业内资深的 OpenResty 技术专家,分享 OpenResty 实战经验,增进 OpenResty 使用者的交流与学习,推动 OpenResty 开源项目的发展。活动已经在深圳、北京两地举办,未来将陆续在武汉、上海、杭州、成都等城市巡回举办。

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

罗玉杰,京东云技术专家,10 余年 CDN、流媒体行业从业经验,热衷于开源软件的开发与研究,对 OpenResty、Nginx 模块开发有较深入的研究,熟悉 CDN 架构和主流流媒体协议。

以下是分享全文:

大家下午好,我是来自京东云的罗玉杰,今天给大家分享的主题是 《OpenResty 在直播场景中的应用》。

项目需求

京东云前期的服务是基于 Nginx 二次开发的,之后因为要对接上云的需求,于是新做了两个服务,一个是对接云存储的上传服务,另一个是偏业务层的直播时移回看服务。项目的需求是做视频数据上云,主要是视频的相关数据对接云存储,需求的开发周期很紧,基本上是以周为单位。

我们之前的服务用 C 、C++ 开发,但 C 和 C++ 的开发周期很长。我们发现这个项目基于 OpenResty 开发是非常适合的,可以极大地缩短开发周期,同时提高运行效率,并且 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。

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

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

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

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

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

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

上传任务成功之后,会发一个 Complete 消息,然后文件就认为是成功了,成功之后就会合并成一个新的文件,对外生成一个可用的大文件。

HLS 协议

HLS 协议,全称是 HTTP LIVE STREAMING 协议,是由苹果推出的,可读性很强。里面的每一个片都是一个 HTTP 请求,整个文本协议就是一个索引。

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

上图是每一个视频段的时长,这个是 8 秒是视频的最大长度。直播的应用中会有一个 Sequence 从零开始递增的,如果有一个新片,就会把旧片去掉,把新的加上去,并增加 Sequence。

任务队列、均衡、流控

下面再介绍一下具体的功能实现,任务收到请求之后不是直接处理,而是异步处理的。先把请求分发到各个 Worker 的私有队列,分发算法是用的 crc32,因为 crc32 足够快、足够轻量,基于一个 key 视频流会有域名、App、stream,再加上 TS 的文件名称。这样分发可以很好地做一次负载均衡。基于这个任务队列,可以处理大量的突发请求,如果突然有了数倍的请求,可以把这些消息发到 Redis 里,由 Redis 存储这些请求。每个 Worker 会同步进行处理,把 TS 片上传,上传完之后再生成 M3U8 文件。我们现在对后端固定了连接数,一个 woker 一个链接,因为存储集群的连接数量是有限的,现在采取一个简单策略,后端能处理请求多快,就发送多快,处理完之后可以马上发送下一个。因任务队列是同步处理,是同步非阻塞的,不会发送超过后端的处理能力。

我们未来准备进行优化的方向就是把任务队列分成多个优先级,高优先级的先处理,低优先级的降级处理。比如我们线上遇到的一些视频流,它不太正常会大量的切小,比如正常视频 10 秒一片,而它 10 毫秒就一片,这样我们会把它的优先级降低,防止异常任务导致正常任务不能合理地处理。以后就是要实现可以动态调解链接数、请求速率和流量。如果后端的处理能力很强,可以动态增长一些链接数和请求速率,一旦遇到瓶颈后可以动态收缩。

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

任务分发比较简单,主要就是上面的三行代码,每一个 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 小时都有流的,用户遇到了一个断洞,可以跳过看后面的视频,时移不需要等待,并且用户网络短暂异常时不会跳片。

点击观看演讲视频和 PPT~

OpenResty 在直播场景中的应用

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

刷屏

刷屏

[美] 凯文•阿洛卡 / 侯奕茜、何语涵 / 中信出版社 / 2018-10-1 / 68.00

1. YouTube流行趋势经理,解密如何打造爆款视频 在视频时代,制造互动,才能创造潮流 用户不再是被动的观众,而是主动的传播者 2. 《刷屏》以行内人视角解读: 病毒视频 粉丝经济 网红产业 平台如何为内容创作者赋能 3. 你是否常常被病毒视频刷屏?你是否觉得很多网红火爆到“无法用常理解释”? 视频时代已经到来,我们每天观看网络......一起来看看 《刷屏》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具