成人网站功用进步 20 倍之经历谈 [Python]

色情业是个大职业。互联网上没有多少网站的流量能和最大的色情网站相匹敌。

要搞定这巨大的流量很难。更困难的是,在色情网站上供给的许多内容都是低推迟的实时流媒体而不是简略的静态视频。可是关于一切碰到过的应战,我很少看到有搞定过它们的开发人员写的东西。所以我决议把自己在这方面的经历写出来。

问题是什么?

几年前,我正在为其时全世界拜访量排名26的网站作业 — 这儿不是说的色情网站排名,而是全世界排名。

其时,该网站经过RTMP(Real Time Messaging protocol)协议呼应对色情流媒体的恳求。更详细地说,它运用了Adobe的FMS(Flash Media Server)技能为用户供给实时流媒体。根本进程是这样的:

用户恳求拜访某个实时流媒体

服务器经过一个RTMP session呼应,播映恳求的视频片段

因为某些原因,FMS对咱们并不是一个好的挑选,首要是它的本钱,包含了购买以下两者:

为每一台运转FMS的服务器购买Windows的版权

大约4000美元一个的FMS特定版权,因为咱们的规划,咱们有必要购买的版权量数以百计,并且每天都在添加。

一切这些费用开端不断累积。放下本钱不提,FMS也是一个比较挫的产品,特别是在它的功用方面(我过一会再详细说这个问题)。所以我决议扔掉FMS,自己从头开端写一个自己的RTMP解析器。

终究,我总算把咱们的服务功率进步了大约20倍。

开端

这儿涉及到两个核心问题:首要,RTMP和其他的Adobe协议及格局都不是敞开的,这就很难运用它们。要是对文件格局都一窍不通,你怎么能对它进 行反向工程或许解析它呢?走运的是,有一些反向工程的测验已经在揭露范畴呈现了(并不是Adobe出品的,而是osflash.org,它破解了一些协 议),咱们的作业便是根据这些效果。

注:Adobe后来发布了所谓的“标准阐明书”,比起在非Adobe供给的反向工程wiki和文档中发表的内容,这个阐明书里也没有啥新东西。他们 给的标准阐明书的质量之低质到达了荒唐的地步,近乎不可能经过该阐明书来运用它们的库。并且,协议自身看起来常常也是有意做成具有误导性的。例如:

他们运用29字节的整形数。

他们在协议头上一切当地都选用低地址寄存最高有用字节(big endian)的格局,除了在某一个字段(并且未标明)上选用低地址寄存最低有用字节(little endian)的格局。

他们在传输9K的视频时,不吝消耗核算才能去紧缩数据削减空间,这根本上是没含义的,因为他们这么折腾一次也便是削减几位或几个字节,对这样的一个文件巨细能够忽略不计了。

还有,RTMP是高度以session为导向的,这使得它根本上不可能对流进行组播。抱负状态下,假如多个用户要求观看同一个实时视频流,咱们能够 直接向他们传回指向单个session的指针,在该session里传输这个视频流(这便是组播的概念)。可是用RTMP的话,咱们有必要为每一个要求拜访 特定流的用户创立全新的一个实例。这是彻底的糟蹋。

我的解决办法

想到了这些,我决议把典型的呼应流从头打包和解析为FLV“标签”(这儿的“标签”指某个视频、音频或许元数据)。这些FLV标签能够在RTMP下顺利地传输。

这样一个办法的优点是:

咱们只需求给流从头打包一次(从头打包是一个噩梦,因为短少标准阐明,还有前面提到的厌恶协议)。

经过套用一个FLV头,咱们能够在客户端之间顺利地重用任何流,而用内部的FLV标签指针(配以某种声明其在流内部切当方位的位移值)就能够拜访到实在的内容。

我一开端用我其时最了解的C言语进行开发。一段时间后,这个挑选变得麻烦了,所以我开端学习Python并移植我的C代码。开发进程加快了,但在做 了一些演示版别后,我很快遇到了资源干涸的问题。Python的socket处理并不合适处理这些类型的状况,详细说,咱们发现在自己的Python代码 里,每个action都进行了屡次体系调用和context切换,这添加了巨大的体系开支。

改善功用:混合运用Python和C

在对代码进行整理之后,我挑选将功用最要害的函数移植到内部彻底用C言语编写的一个Python模块中。这根本是底层的东西,详细地说,它利用了内核的epoll机制供给了一个O(log n)的算法复杂度。

在异步socket编程方面,有一些机制能够供给有关特定socket是否可读/可写/犯错之类的信息。曩昔,开发人员们能够用select()系 统调用获取这些信息,但很难大规划运用。Poll()是更好的挑选,但它依然不够好,因为你每次调用的时分都要传递一大堆socket描述符。

Epoll的奇特之处在于你只需求挂号一个socket,体系会记住这个特定的socket并处理一切内部的凌乱的细节。这样在每次调用的时分就没 有传递参数的开支了。并且它适用的规划也大有可观,它只回来你关怀的那些socket,比较用其他技能时有必要从10万个socket描述符列表里挨个查看 是否有带字节掩码的工作,其优越性真是非同寻常啊。

不过,为了功用的进步,咱们也付出了价值:这个办法选用了彻底和曾经不同的规划形式。该网站曾经的办法是(假如我没记错的话)单个原始进程,在接纳和发送时会堵塞。我开发的是一套工作驱动计划,所以为了习惯这个新模型,我有必要重构其他的代码。

详细地说,在新办法中,咱们有一个主循环,它按如下办法处理接纳和发送:

接纳到的数据(作为音讯)被传递到RTMP层

RTMP包被解析,从中提取出FLV标签

FLV数据被传输到缓存和组播层,在该层对流进行安排并填充到底层传输缓存中

发送程序为每个客户端保存一个结构,包含了终究一次发送的索引,并尽可能多地向客户端传送数据

这是一个翻滚的数据窗口,并包含了某些试探性算法,当客户端速度太慢无法接纳时会丢掉一些帧。整体来说运转的很好。

体系层级,架构和硬件问题

可是咱们又遇到别的一个问题:内核的context切换成为了一个担负。成果,咱们挑选每100毫秒发送一次而不是实时发送。这样能够把小的数据包汇总起来,也避免了context切换的爆破式呈现。

或许更大的一个问题在于服务器架构方面:咱们需求一个具有负载均衡和容错才能的服务器集群,究竟因为服务器功用反常而失掉用户不是件好玩的工作。一 开端,咱们选用了专职总管服务器的办法,它指定一个”总管“担任经过猜测需求来发生和消除播映流。这个办法富丽丽地失利了。实际上,咱们测验过的每个办法 都适当明显地失利了。终究,咱们选用了一个相对暴力的办法,在集群的各个节点之间随机地同享播映的流,使流量根本平衡了。

这个办法是有用的,可是也有一些缺乏:尽管一般状况下它处理的很好,咱们也碰到了当一切网站用户(或许适当大比例的用户)观看单个播送流的时分,性 能会变得十分糟糕。好音讯是,除了一次商场宣扬活动(marketing campaign)之外,这种状况再也没呈现过。咱们布置了别的一套独自的集群来处理这种状况,但实在的状况是咱们先剖析了一番,觉得为了一次商场活动而 献身付费用户的体会是说不曩昔的,实际上,这个事例也不是一个实在的工作(尽管说能处理一切幻想得到的状况也是很好的)。

定论

这儿有终究成果的一些统计数字:每天在集群里的流量在峰值时是大约10万用户(60%负载),平均是5万。我管理了2个集群(匈牙利和美国),每个 里有大约40台服务器一起承当这个负载。这些集群的总带宽大约是50 Gbps,在负载到达峰值时大约运用了10 Gbps。终究,我努力做到了让每台服务器轻松地能供给10 Gbps带宽,也就等于一台服务器能够接受30万用户一起观看视频流。

已有的FMS集群包含了超越200台服务器,我只需求15台就能够替代他们,并且其间只要10台在实在供给服务。这就等于200除以10,等于20 倍的功用进步。大约我在这个项目里最大的收成便是我不该让自己受阻于学习新技能的困难。详细说来,Python、转码、面向对象编程,这些都是我在做这个 项目之前短少专业经历的概念。

这个信仰,以及完成你自己的计划的决心,会给你带来很大的报答。

【1】后来,当咱们把新代码投入生产,咱们又遇到了硬件问题,因为咱们运用老的sr2500 Intel架构服务器,因为它们的PCI总线带宽太低,不能支撑10 Gbit的以太网卡。没辙,咱们只好把它们用在1-4×1 Gbit的以太网池中(把多个网卡的功用汇总为一个虚拟网卡)。终究,咱们获得了一些更新的sr2600 i7 Intel架构服务器,它们经过光纤到达了无功用损耗的10 Gbps带宽。一切上述汇总的成果都是根据这样的硬件条件来核算的。


上一篇:python数据库连接池技能总结
下一篇:python动态捕获反常

PythonTab微信大众号:

Python技能交流合作群 ( 请勿加多个群 ):

群1: 87464755

群2: 333646237

群3: 318130924

群4: 385100854