我如何通过Python视频流使Porn效率提高20倍

本文概述

  • 介绍
  • 有什么问题?
  • 入门
  • 我的多播视频流解决方案
  • 提高视频流性能:混合使用Python, RTMP和C
  • 系统级, 体系结构和硬件问题
  • 总结
介绍 色情是一个大行业。互联网上没有什么网站可以与最大竞争对手的流量相媲美。
并且要处理这种巨大的流量非常困难。更麻烦的是, 色情网站提供的大部分内容都是由低延迟的实时视频流组成, 而不是简单的静态视频内容。但是对于所涉及的所有挑战, 我很少了解承担这些挑战的python开发人员。因此, 我决定写自己的工作经历。
有什么问题? 几年前, 我在世界上访问量排名第26(当时)的网站上工作, 而不仅仅是色情行业:世界。
当时, 该网站使用实时消息协议(RTMP)满足了色情视频流请求。更具体地说, 它使用了Adobe构建的Flash Media Server(FMS)解决方案, 为用户提供实时流。基本过程如下:
  1. 用户请求访问某些直播
  2. 服务器回复播放所需素材的RTMP会话
有两个原因, 从成本开始, 包括购买以下两者, FMS对我们来说不是一个好选择:
  1. 我们在其上运行FMS的每台计算机的Windows许可证。
  2. 约$ 4K FMS专用许可, 由于我们的规模, 我们不得不购买其中的数百个(每天都更多)。
所有这些费用开始增加。除了成本, FMS还是缺少的产品, 特别是在功能上(稍有更多)。因此, 我决定取消FMS并从头开始编写自己的Python RTMP解析器。
最后, 我设法使我们的服务效率提高了大约20倍。
入门 涉及两个核心问题:首先, RTMP和其他Adobe协议和格式未公开(即公开可用), 这使它们难以使用。如何以一种你一无所知的格式反向或解析文件?幸运的是, 我们的工作基于公共领域(不是由Adobe而是由OS Flash组成的, 现已不存在的组织)做出的一些逆转努力。
注意:Adobe后来发布了” 规范” , 其中所包含的信息没有在非Adobe生产的反向Wiki和文档中公开的信息。他们的(Adobe)规范质量低得令人荒唐, 几乎无法实际使用他们的库。此外, 该协议本身有时似乎是有意误导的。例如:
  1. 他们使用29位整数。
  2. 它们包括带有大字节序格式的协议标头, 处处都是大字节序格式, 除了特定(尚未标记)字段(小字节序)外。
  3. 他们在传输9k视频帧时以计算能力为代价将数据压缩到更少的空间中, 这几乎没有意义, 因为它们一次赚回了比特或字节, 对于这种文件大小而言, 这是微不足道的。
其次:RTMP高度面向会话, 这实际上使得组播输入流几乎不可能。理想情况下, 如果多个用户想要观看相同的实时流, 我们可以将它们传递回指向直播该流的单个会话的指针(这将是多播视频流)。但是使用RTMP, 我们必须为每个想要访问的用户创建流的全新实例。这完全是浪费。
我如何通过Python视频流使Porn效率提高20倍

文章图片
我的多播视频流解决方案 考虑到这一点, 我决定将典型的响应流重新打包/解析为FLV” 标签” (“ 标签” 只是一些视频, 音频或元数据)。这些FLV标签可以在RTMP内传输, 几乎没有问题。
这种方法的好处:
  1. 我们只需要重新打包一次流(由于缺少上面概述的规范和协议怪癖, 重新打包是一场噩梦)。
  2. 我们可以为客户之间的任何流重新使用, 只需为它们提供FLV标头即可, 而只需要为它们提供很少的问题, 而指向FLV标签的内部指针(以及某种偏移量则表明它们在流中的位置)允许访问内容。
我开始用当时最熟悉的语言进行开发:C.随着时间的流逝, 这种选择变得很繁琐。因此我在移植C代码的同时开始学习Python的基础知识。开发过程加快了速度, 但是经过几个演示之后, 我很快遇到了资源枯竭的问题。 Python的套接字处理并不是要处理这些类型的情况:具体来说, 在Python中, 我们发现自己每个动作都要进行多个系统调用和上下文切换, 从而增加了大量的开销。
提高视频流性能:混合使用Python, RTMP和C 在对代码进行性能分析之后, 我选择将性能关键的功能移到完全用C编写的Python模块中。这是相当底层的内容:具体地说, 它利用内核的epoll机制来提供对数增长顺序。
在异步套接字编程中, 有一些工具可以为你提供给定套接字是否可读/可写/错误填充的信息。过去, 开发人员使用select()系统调用来获取此信息, 该信息的伸缩性很差。 Poll()是select的一个更好的版本, 但是它仍然没有那么好, 因为你每次调用时都必须传入一堆套接字描述符。
Epoll令人惊叹, 因为你要做的就是注册一个套接字, 系统会记住该套接字, 并在内部处理所有详细信息。因此, 每次调用都没有传递参数的开销。它还具有更好的伸缩性, 并且只返回你关心的套接字, 这比遍历100k套接字描述符的列表来查看它们是否具有带位掩码的事件要好得多, 如果使用其他解决方案, 则需要这样做。
但是, 为了提高性能, 我们付出了代价:这种方法采用了与以前完全不同的设计模式。该网站以前的方法(如果我没记错的话)是一个整体流程, 该流程在接收和发送时受阻;我当时正在开发一个事件驱动的解决方案, 因此我必须重构其余的代码以适应这个新模型。
具体来说, 在我们的新方法中, 我们有一个主循环, 它按以下方式处理接收和发送:
我如何通过Python视频流使Porn效率提高20倍

文章图片
  1. 接收到的数据(作为消息)一直传递到RTMP层。
  2. 解剖RTMP并提取FLV标签。
  3. FLV数据被发送到缓冲和多播层, 由缓冲层和多播层组织流并填充发送者的低级缓冲区。
  4. 发送方为每个客户端保留一个结构, 并带有最后发送的索引, 并尝试向客户端发送尽可能多的数据。
这是一个滚动的数据窗口, 其中包含一些启发式方法, 以在客户端太慢而无法接收时丢弃帧。事情进展顺利。
系统级, 体系结构和硬件问题 但是我们遇到了另一个问题:内核的上下文切换正成为一种负担。结果, 我们选择仅每100毫秒写入一次, 而不是立即写入。这聚集了较小的数据包并防止了上下文切换的爆发。
也许更大的问题在于服务器体系结构领域:我们需要一个负载平衡和具有故障转移功能的群集-由于服务器故障而导致用户流失并不是一件好事。最初, 我们采用了独立导演方法, 即指定的” 导演” 将尝试通过预测需求来创建和销毁广播公司的供稿。这失败了。实际上, 我们尝试的所有操作都相当失败。最后, 我们选择了一种相对蛮力的方法, 在群集节点之间随机共享广播者, 以平衡流量。
这行得通, 但有一个缺点:尽管一般情况下处理得很好, 但当站点上的每个人(或不成比例的用户)观看单个广播电台时, 我们看到了糟糕的性能。好消息:这绝不会在营销活动之外发生。我们实施了一个单独的集群来处理这种情况, 但实际上, 我们认为破坏付费用户的体验以进行营销工作是没有意义的—实际上, 这并不是真正的情况(尽管处理所有可以想象的情况都很好)案件)。
总结 来自最终结果的一些统计数据:群集上的每日流量在高峰时(负载为60%)约为10万用户, 平均约为50, 000。我管理两个集群(HUN和US);他们每个人处理大约40台机器来分担负载。群集的总带宽约为50 Gbps, 在峰值负载时, 它们使用的带宽约为10 Gbps。最后, 我设法轻松地将每台机器的速度提高了10 Gbps。理论上1, 这个数字可能高达每台机器30 Gbps, 这意味着大约30万用户同时从一台服务器观看流。
现有的FMS集群包含200台以上的计算机, 可以替换为我的15台计算机, 其中只有10台可以完成任何实际工作。这使我们大致提高了200/10 = 20倍。
我可能从Python视频流传输项目中获得的最大收获是, 我不应该因为必须学习新技能而受挫。尤其是, Python, 转码和面向对象的编程都是所有概念, 在从事此多播视频项目之前, 我具有非常专业的经验。
那, 以及推出自己的解决方案可能会付出巨大的代价。
【我如何通过Python视频流使Porn效率提高20倍】1后来, 当我们将代码投入生产时, 我们遇到了硬件问题, 因为我们使用的旧sr2500 Intel服务器由于PCI带宽低而无法处理10 Gbit以太网卡。取而代之的是, 我们将它们用于1-4× 1 Gbit以太网绑定(将多个网络接口卡的性能汇总为虚拟卡)。最终, 我们得到了一些较新的sr2600 i7 Intel, 它们在光学上可提供10 Gbps的传输速度, 而没有任何性能纠结。所有预计的计算都参考此硬件。

    推荐阅读