我要投稿
  • 您當前的位置:中國教育資源網 -> 技術教程 -> 服務器網絡 -> 服務器教程 -> LinuxBSD教程 -> 教程內容
  • [ 收藏本頁教程 ]
  • 成人網站性能提升 20 倍之經驗談 [Python]

    教程作者:本站    教程來源:本站整理   教程欄目:LinuxBSD教程    收藏本頁
    摘要: 色情業是個大行業。互聯網上沒有多少網站的流量能和最大的色情網站相匹敵。要搞定這巨大的流量很難。更困難的是,在色情網站上提供的很多內容都是低延遲的實時流媒體而不是簡單的靜態視頻。但是對于所有碰到過的挑戰 ...

    色情業是個大行業。互聯網上沒有多少網站的流量能和最大的色情網站相匹敵。

    要搞定這巨大的流量很難。更困難的是,在色情網站上提供的很多內容都是低延遲的實時流媒體而不是簡單的靜態視頻。但是對于所有碰到過的挑戰,我很少看到有搞定過它們的開發人員寫的東西。所以我決定把自己在這方面的經驗寫出來。 

     

    問題是什么?

    幾年前,我正在為當時全世界訪問量排名26的網站工作 — 這里不是說的色情網站排名,而是全世界排名。

    當時,該網站通過RTMP(Real Time Messaging protocol)協議響應對色情流媒體的請求。更具體地說,它使用了Adobe的FMS(Flash Media Server)技術為用戶提供實時流媒體。基本過程是這樣的:

    1. 用戶請求訪問某個實時流媒體
    2. 服務器通過一個RTMP session響應,播放請求的視頻片段

    因為某些原因,FMS對我們并不是一個好的選擇,首先是它的成本,包括了購買以下兩者:

    1. 為每一臺運行FMS的服務器購買Windows的版權
    2. 大約4000美元一個的FMS特定版權,由于我們的規模,我們必須購買的版權量數以百計,而且每天都在增加。

    所有這些費用開始不斷累積。撇開成本不提,FMS也是一個比較挫的產品,特別是在它的功能方面(我過一會再詳細說這個問題)。所以我決定拋棄FMS,自己從頭開始寫一個自己的RTMP解析器。

    最后,我終于把我們的服務效率提升了大約20倍。

    開始

    這里涉及到兩個核心問題:首先,RTMP和其他的Adobe協議及格式都不是開放的,這就很難使用它們。要是對文件格式都一無所知,你如何能對它進 行反向工程或者解析它呢?幸運的是,有一些反向工程的嘗試已經在公開領域出現了(并不是Adobe出品的,而是osflash.org,它破解了一些協 議),我們的工作就是基于這些成果。

    注:Adobe后來發布了所謂的“規格說明書”,比起在非Adobe提供的反向工程wiki和文檔中披露的內容,這個說明書里也沒有啥新東西。他們 給的規格說明書的質量之低劣達到了荒謬的境地,近乎不可能通過該說明書來使用它們的庫。而且,協議本身看起來常常也是有意做成具有誤導性的。例如:

    1. 他們使用29字節的整形數。
    2. 他們在協議頭上所有地方都采用低地址存放最高有效字節(big endian)的格式,除了在某一個字段(而且未標明)上采用低地址存放最低有效字節(little endian)的格式。
    3. 他們在傳輸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描述符列表里挨個檢查 是否有帶字節掩碼的事件,其優越性真是非同小可啊。

    不過,為了性能的提高,我們也付出了代價:這個方法采用了完全和以前不同的設計模式。該網站以前的方法是(如果我沒記錯的話)單個原始進程,在接收和發送時會阻塞。我開發的是一套事件驅動方案,所以為了適應這個新模型,我必須重構其他的代碼。

    具體地說,在新方法中,我們有一個主循環,它按如下方式處理接收和發送:

    1. 接收到的數據(作為消息)被傳遞到RTMP層
    2. RTMP包被解析,從中提取出FLV標簽
    3. FLV數據被傳輸到緩存和組播層,在該層對流進行組織并填充到底層傳輸緩存中
    4. 發送程序為每個客戶端保存一個結構,包含了最后一次發送的索引,并盡可能多地向客戶端傳送數據

    這是一個滾動的數據窗口,并包含了某些試探性算法,當客戶端速度太慢無法接收時會丟棄一些幀。總體來說運行的很好。 

     

    系統層級,架構和硬件問題

    但是我們又遇到另外一個問題:內核的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帶寬。所有上述匯總的結果都是基于這樣的硬件條件來計算的。

    我要投稿   -   廣告合作   -   關于本站   -   友情連接   -   網站地圖   -   聯系我們   -   版權聲明   -   設為首頁   -   加入收藏   -   網站留言
    Copyright © 2009 - 20012 www.obscurenation.com All Rights Reserved.中國教育資源網 版權所有  
     
    彩16app 文登市 | 扎囊县 | 中卫市 | 万全县 | 南陵县 | 绵竹市 | 焦作市 | 绥滨县 | 阿合奇县 | 株洲市 | 阳朔县 | 含山县 | 富宁县 | 双城市 | 甘谷县 | 栖霞市 | 天水市 | 宝山区 | 枣阳市 | 广汉市 | 万载县 | 烟台市 | 大埔区 | 英德市 | 邵阳市 | 汉沽区 | 海晏县 | 嘉善县 | 祁连县 | 林口县 | 隆回县 | 兴海县 | 河北省 | 沙河市 | 徐汇区 | 聊城市 | 望谟县 | 隆林 | 文化 | 濮阳县 | 大庆市 | 黑水县 | 安徽省 | 锦屏县 | 永善县 | 会理县 | 庆元县 | 靖江市 | 松原市 | 建瓯市 | 隆德县 | 门源 | 庆元县 | 泸溪县 | 凭祥市 | 织金县 | 昌乐县 | 威宁 | 南雄市 | 西和县 | 德安县 | 和龙市 | 阜康市 | 横峰县 | 大田县 | 安阳市 | 扎囊县 | 泽库县 | 苏尼特左旗 | 冀州市 | 雷州市 | 平利县 | 合川市 | 苏州市 | 沙坪坝区 | 翼城县 | 玉树县 | 敦煌市 | 青铜峡市 | 襄垣县 | 台南县 | 紫金县 | 桦川县 | 海安县 | 界首市 | 德阳市 | 伊通 | 夏邑县 | 兴宁市 | 平江县 | 赣州市 | 兴义市 | 监利县 | 方正县 | 鄯善县 | 长汀县 | 古浪县 | 阳高县 | 文山县 | 宣化县 | 牡丹江市 | 湖州市 | 木兰县 | 淮北市 | 商洛市 | 章丘市 | 曲靖市 | 资中县 | 合作市 | 晴隆县 | 三亚市 | 尉犁县 | 新竹县 | 宜昌市 | 哈巴河县 | 肃宁县 | 萍乡市 | 西青区 | 靖宇县 | 新密市 | 葵青区 | 舟曲县 | 大同市 | 藁城市 | 江口县 | 峨山 | 武城县 | 巴彦淖尔市 | 葫芦岛市 | 慈利县 | 桦南县 | 阜城县 | 饶阳县 | 澄迈县 | 鄂托克前旗 | 昂仁县 | 长阳 | 驻马店市 | 资源县 | 侯马市 | 伊金霍洛旗 | 绵阳市 | 黑水县 | 濮阳县 | 东港市 | 甘孜县 | 靖安县 | 渭源县 | 北安市 | 腾冲县 | 金湖县 | 长沙市 | 天全县 | 宽城 | 绥滨县 | 伊宁县 | 桓仁 | 永德县 | 镶黄旗 | 德化县 | 琼海市 | 通辽市 | 平遥县 | 青铜峡市 | 手游 | 东明县 | 鄂尔多斯市 | 东宁县 | 远安县 | 潮安县 | 台南县 | 襄垣县 | 闻喜县 | 永城市 | 辽阳市 | 黑山县 | 五大连池市 | 马鞍山市 | 大同市 | 沧源 | 开封县 | 东宁县 | 永宁县 | 周宁县 | 南澳县 | 双辽市 | 桂阳县 | 石家庄市 | 旌德县 | 区。 | 阜新 | 阿瓦提县 | 杂多县 | 简阳市 | 海口市 | 黄梅县 | 蕉岭县 | 安福县 | 保德县 | 田东县 | 临城县 | 南岸区 | 祁门县 | 兴仁县 | 锡林郭勒盟 | 永靖县 | 仙居县 | 永胜县 | 正定县 | 探索 | 老河口市 | 全州县 | 山丹县 | 邹平县 | 宝山区 | 宁河县 | 和硕县 | 巢湖市 | 泾源县 | 库尔勒市 | 洛隆县 | 精河县 | 花垣县 | 阿克陶县 | 龙井市 | 曲水县 | 积石山 | 丁青县 | 莎车县 | 汤原县 | 漠河县 | 武川县 | 佛坪县 | 平江县 | 射阳县 | 达日县 | 繁昌县 | 新竹市 | 筠连县 | 定襄县 | 罗山县 | 葵青区 | 高邮市 | 嘉鱼县 | 四川省 | 保亭 | 明星 | 福泉市 | 新乡县 | 陵川县 | 易门县 |