FIFA 2014年世界杯直播流的架构

Leandro  1我们之前接到了给FIFA 2014年世界杯做推流的任务,我觉得这段经历很值得拿出来来共享。这篇文章是对架构,构成部分,经历的痛苦,学到的东西,开放的资源等等的一个简单概述。

一些数字

  • 德国7:1战胜巴西 (是的,我们觉得并不光荣)
  • 单场比赛同时50万的观众在线 – 阿根廷对阵瑞士
  • 单场比赛达到580Gbps的速率 – 阿根廷对阵瑞士
  • 整届世界杯大约等同于1600年的总观看时长

核心概述

项目的目的是接收一个输入流,为成百上千人产生HLS输出流,并且给最终用户提供极好的观看体验:

  1. 提取RTMP输入流
  2. 产生HLS并且将其送入Cassandra
  3. 从Cassandra中提取二进制数据和元数据,并且用Nginx+lua重建HLS播放列表
  4. 以可伸缩的方式(scalable way)服务并缓存直播内容
  5. 设计并实现播放器

如果您想要搞清楚为什么我们选择使用HLS,请浏览这个陈述(只有葡萄牙语)提示:有时我们需要从零开始重建一些东西。

输入

直播流以RTMP的形式进入我们的服务器,我们使用EvoStream(现在我们改使用nginx-rtmp)来接收此输入并且产生HLS输出给已知的文件库。之后我们有一些在同一台设备中运行的python后台进程,来监控这个已知的文件库及解析m3u8,并且向Cassandra推送数据。

为了能够观察到文件修改,以及能够被这些事件所提醒,我们最初尝试使用看门狗来达到这个目的,但是出于某些原因我们并不能使其工作的像我们预期的那么快,所以我们后来改为pyinotify。

另外一个我们克服的困难是把Python程序规模适用于x CPU核中。最终,我们通过创建多个Python进程,以及使用异步执行(async execution),解决了问题。

提示:也许最佳的语言/工具在另外一个城堡中。(有更好的解决方法)

储存

我们之前使用的是Redis来储存直播流的数据,但是我们认为需要使用Cassandra来更简单地提供DVR功能(尽管这样,我们依旧总是使用Redis)。Cassandra的响应时间随负载而增加到一个确定点,在这个确定点处,用户处开始超时,并且视频回放被完全停止。

我们当初以类似排队(Queue-like)的方式来使用它,但却变成了一个反模式。后来我们将我们的数据去归一化,并且也改变为LeveledCompactionStrategy,以及将durable_writes设为false,因为我们可以将我们的直播流视为临时数据。

最后,但是最为重要的,既然我们已经知道了一个播放列表所能拥有的最大大小,我们就能够指定开始列(filtering with id > minTimeuuid(now – playlist_duration))。这点能够非常有效地缓和读取的墓碑效应。做出这些改变之后,我们就可以为我们99%的百分位获得大约10ms的时延。

提示:限制你的查询 + 去归一化你的数据 + 传送数据给graphite + 使用SSD。

输出

通过所有的数据和元数据,我们可以建立HLS清单并且服务视频块。我们但是所努力的唯一一件事就是我们不想增添一个额外的服务器来取得和建立这个清单。

因为我们已经在Nginx+Lua中投入了大量的精力,我们认为使用lua来获取及建立此清单是可行的。这就是一个如何建立给Cassandra的lua驱动器并且使用它的问题了。这个方法(重建此清单)的一个好处是,最终我们意识到了我们已经准备好了来服务DASH

提示测试你的lua scripts + 检查lua的全局vars + 反复检查你的缓存配置

播放器

2

为了提供更好的观看体验,我们选择建立Clappr,一个可扩展的开源HTML5视频播放器。通过Clappr – 以及一些像PiP(画中画)的自定义拓展和多角度回放 – 我们就可以给我们的用户提供极佳的观看体验。

提示:从一开始开源 + 关注流的问题 -> 承诺FIX#123 (open source it from day 0 + follow to flow issue -> commit FIX#123)

Sauron

3

为了监管所有这些系统,我们建立了一个监控仪表板,其中利用了几乎开源的项目,比如:logstash,elastic search,graphite,graphana,kibana,seyren,angular,mongo,redis,rails以及很多其他项目。

提示:在graphite和elasticsearch中使用SSD。

额外的

尽管我们没有将整个方案开源,但是您可以查阅其中大部分的内容:

填写常用邮箱,接收社区更新

WebRTC 中文社区由

运营