Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产在视频线精品视频,2021国产视频,好男人视频社区2019在线观看

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          百億流量系統,是如何從0開始搭建的?

          百億流量系統,是如何從0開始搭建的?
          作者:xiaojiaqi
          來源:https://github.com/xiaojiaqi/10billionhongbaos
          

          . 前言

          前幾天,偶然看到了 《扛住100億次請求——如何做一個“有把握”的春晚紅包系統”》一文,看完以后,感慨良多,收益很多。正所謂他山之石,可以攻玉,雖然此文發表于2015年,我看到時已經過去良久,但是其中的思想仍然是可以為很多后端設計借鑒。

          同時作為一微信后端工程師,看完以后又會思考,學習了這樣的文章以后,是否能給自己的工作帶來一些實際的經驗呢?所謂紙上得來終覺淺,絕知此事要躬行,能否自己實踐一下100億次紅包請求呢?否則讀完以后腦子里能剩下的東西 不過就是100億 1400萬QPS整流 這樣的字眼,剩下的文章將展示作者是如何以此過程為目標,在本地環境的模擬了此過程。

          實現的目標:單機支持100萬連接,模擬了搖紅包和發紅包過程,單機峰值QPS 6萬,平穩支持了業務。

          注:本文以及作者所有內容,僅代表個人理解和實踐,過程和微信團隊沒有任何關系,真正的線上系統也不同,只是從一些技術點進行了實踐,請讀者進行區分。


          2. 背景知識


          • QPS:Queries per second 每秒的請求數目
          • PPS:Packets per second 每秒數據包數目
          • 搖紅包:客戶端發出一個搖紅包的請求,如果系統有紅包就會返回,用戶獲得紅包
          • 發紅包:產生一個紅包里面含有一定金額,紅包指定數個用戶,每個用戶會收到紅包信息,用戶可以發送拆紅包的請求,獲取其中的部分金額。


          3. 確定目標

          在一切系統開始以前,我們應該搞清楚我們的系統在完成以后,應該有一個什么樣的負載能力。

          3.1 用戶總數

          通過文章我們可以了解到接入服務器638臺,服務上限大概是14.3億用戶, 所以單機負載的用戶上限大概是14.3億/638臺=228萬用戶/臺。但是目前中國肯定不會有14億用戶同時在線,參考 http://qiye.qianzhan.com/show/detail/160818-b8d1c700.html的說法,2016年Q2 微信用戶大概是8億,月活在5.4 億左右。所以在2015年春節期間,雖然使用的用戶會很多,但是同時在線肯定不到5.4億。

          3.2. 服務器數量

          一共有638臺服務器,按照正常運維設計,我相信所有服務器不會完全上線,會有一定的硬件冗余,來防止突發硬件故障。假設一共有600臺接入服務器。

          3.3 單機需要支持的負載數

          每臺服務器支持的用戶數:5.4億/600=90萬。也就是平均單機支持90萬用戶。如果真實情況比90萬更多,則模擬的情況可能會有偏差,但是我認為QPS在這個實驗中更重要。

          3.4. 單機峰值QPS

          文章中明確表示為1400萬QPS.這個數值是非常高的,但是因為有600臺服務器存在,所以機的QPS為 1400萬/600=約為2.3萬QPS, 文章曾經提及系統可以支持4000萬QPS,那么系統的QPS 至少要到4000萬/600=約為 6.6萬, 這個數值大約是目前的3倍,短期來看并不會被觸及。但是我相信應該做過相應的壓力測試。

          3.5. 發放紅包

          文中提到系統以5萬個每秒的下發速度,那么單機每秒下發速度50000/600 =83個/秒,也就是單機系統應該保證每秒以83個的速度下發即可。

          最后考慮到系統的真實性,還至少有用戶登錄的動作,拿紅包這樣的業務。真實的系統還會包括聊天這樣的服務業務。

          最后整體的看一下 100億次搖紅包這個需求,假設它是均勻地發生在春節聯歡晚會的4個小時里,那么服務器的QPS 應該是10000000000/600/3600/4.0=1157. 也就是單機每秒1000多次,這個數值其實并不高。如果完全由峰值速度1400萬消化 10000000000/(1400*10000)=714秒,也就是說只需要峰值堅持11分鐘,就可以完成所有的請求??梢娀ヂ摼W產品的一個特點就是峰值非常高,持續時間并不會很長。


          總結

          從單臺服務器看,它需要滿足下面一些條件:


          1. 支持至少100萬連接用戶
          2. 每秒至少能處理2.3萬的QPS,這里我們把目標定得更高一些 分別設定到了3萬和6萬。
          3. 搖紅包:支持每秒83個的速度下發放紅包,也就是說每秒有2.3萬次搖紅包的請求,其中83個請求能搖到紅包,其余的2.29萬次請求會知道自己沒搖到。當然客戶端在收到紅包以后,也需要確??蛻舳撕头掌鲀蛇叺募t包數目和紅包內的金額要一致。因為沒有支付模塊,所以我們也把要求提高一倍,達到200個紅包每秒的分發速度
          4. 支持用戶之間發紅包業務,確保收發兩邊的紅包數目和紅包內金額要一致。同樣也設定200個紅包每秒的分發速度為我們的目標。


          想完整模擬整個系統實在太難了,首先需要海量的服務器,其次需要上億的模擬客戶端。這對我來說是辦不到,但是有一點可以確定,整個系統是可以水平擴展的,所以我們可以模擬100萬客戶端,在模擬一臺服務器 那么就完成了1/600的模擬。

          和現有系統區別:和大部分高QPS測試的不同,本系統的側重點有所不同。我對2者做了一些對比。


          4. 基礎軟件和硬件


          4.1軟件

          Golang 1.8r3 , shell, python (開發沒有使用c++ 而是使用了golang, 是因為使用golang 的最初原型達到了系統要求。雖然golang 還存在一定的問題,但是和開發效率比,這點損失可以接受)

          服務器操作系統:Ubuntu 12.04

          客戶端操作系統:debian 5.0


          4.2硬件環境

          服務端:dell R2950。8核物理機,非獨占有其他業務在工作,16G內存。這臺硬件大概是7年前的產品,性能應該不是很高要求。

          服務器硬件版本:


          服務器CPU信息:

          客戶端:esxi 5.0 虛擬機,配置為4核 5G內存。一共17臺,每臺和服務器建立6萬個連接。完成100萬客戶端模擬


          5. 技術分析和實現

          5.1) 單機實現100萬用戶連接

          這一點來說相對簡單,筆者在幾年前就早完成了單機百萬用戶的開發以及操作。現代的服務器都可以支持百萬用戶。相關內容可以查看:

          github代碼以及相關文檔:

          https://github.com/xiaojiaqi/C1000kPracticeGuide

          系統配置以及優化文檔:

          https://github.com/xiaojiaqi/C1000kPracticeGuide/tree/master/docs/cn

          5.2) 3萬QPS

          這個問題需要分2個部分來看客戶端方面和服務器方面。


          • 客戶端QPS


          因為有100萬連接連在服務器上,QPS為3萬。這就意味著每個連接每33秒,就需要向服務器發一個搖紅包的請求。因為單IP可以建立的連接數為6萬左右, 有17臺服務器同時模擬客戶端行為。我們要做的就保證在每一秒都有這么多的請求發往服務器即可。

          其中技術要點就是客戶端協同。但是各個客戶端的啟動時間,建立連接的時間都不一致,還存在網絡斷開重連這樣的情況,各個客戶端如何判斷何時自己需要發送請求,各自該發送多少請求呢?

          我是這樣解決的:利用NTP服務,同步所有的服務器時間,客戶端利用時間戳來判斷自己的此時需要發送多少請求。

          算法很容易實現:假設有100萬用戶,則用戶id 為0-999999.要求的QPS為5萬, 客戶端得知QPS為5萬,總用戶數為100萬,它計算 100萬/5萬=20,所有的用戶應該分為20組,如果 time() % 20==用戶id % 20,那么這個id的用戶就該在這一秒發出請求,如此實現了多客戶端協同工作。每個客戶端只需要知道 總用戶數和QPS 就能自行準確發出請求了。

          (擴展思考:如果QPS是3萬 這樣不能被整除的數目,該如何辦?如何保證每臺客戶端發出的請求數目盡量的均衡呢?)


          • 服務器QPS


          服務器端的QPS相對簡單,它只需要處理客戶端的請求即可。但是為了客觀了解處理情況,我們還需要做2件事情。


          • 第一:需要記錄每秒處理的請求數目,這需要在代碼里埋入計數器。
          • 第二:我們需要監控網絡,因為網絡的吞吐情況,可以客觀的反映出QPS的真實數據。為此,我利用python腳本 結合ethtool 工具編寫了一個簡單的工具,通過它我們可以直觀的監視到網絡的數據包通過情況如何。它可以客觀的顯示出我們的網絡有如此多的數據傳輸在發生。


          工具截圖:


          5.3) 搖紅包業務

          搖紅包的業務非常簡單,首先服務器按照一定的速度生產紅包。紅包沒有被取走的話,就堆積在里面。服務器接收一個客戶端的請求,如果服務器里現在有紅包就會告訴客戶端有,否則就提示沒有紅包。

          因為單機每秒有3萬的請求,所以大部分的請求會失敗。只需要處理好鎖的問題即可。

          我為了減少競爭,將所有的用戶分在了不同的桶里。這樣可以減少對鎖的競爭。如果以后還有更高的性能要求,還可以使用 高性能隊列——Disruptor來進一步提高性能。

          注意,在我的測試環境里是缺少支付這個核心服務的,所以實現的難度是大大的減輕了。另外提供一組數字:2016年淘寶的雙11的交易峰值僅僅為12萬/秒,微信紅包分發速度是5萬/秒,要做到這點是非常困難的。(http://mt.sohu.com/20161111/n472951708.shtml)


          5.4) 發紅包業務

          發紅包的業務很簡單,系統隨機產生一些紅包,并且隨機選擇一些用戶,系統向這些用戶提示有紅包。這些用戶只需要發出拆紅包的請求,系統就可以隨機從紅包中拆分出部分金額,分給用戶,完成這個業務。同樣這里也沒有支付這個核心服務。


          5.5)監控

          最后 我們需要一套監控系統來了解系統的狀況,我借用了我另一個項目(https://github.com/xiaojiaqi/fakewechat) 里的部分代碼完成了這個監控模塊,利用這個監控,服務器和客戶端會把當前的計數器內容發往監控,監控需要把各個客戶端的數據做一個整合和展示。同時還會把日志記錄下來,給以后的分析提供原始數據。線上系統更多使用opentsdb這樣的時序數據庫,這里資源有限,所以用了一個原始的方案。

          監控顯示日志大概這樣:


          6. 代碼實現及分析

          在代碼方面,使用到的技巧實在不多,主要是設計思想和golang本身的一些問題需要考慮。

          首先golang的goroutine 的數目控制,因為至少有100萬以上的連接,所以按照普通的設計方案,至少需要200萬或者300萬的goroutine在工作。這會造成系統本身的負擔很重。

          其次就是100萬個連接的管理,無論是連接還是業務都會造成一些心智的負擔。

          我的設計是這樣的:

          首先將100萬連接分成多個不同的SET,每個SET是一個獨立,平行的對象。每個SET 只管理幾千個連接,如果單個SET 工作正常,我只需要添加SET就能提高系統處理能力。

          其次謹慎的設計了每個SET里數據結構的大小,保證每個SET的壓力不會太大,不會出現消息的堆積。

          再次減少了gcroutine的數目,每個連接只使用一個goroutine,發送消息在一個SET里只有一個gcroutine負責,這樣節省了100萬個goroutine。這樣整個系統只需要保留 100萬零幾百個gcroutine就能完成業務。大量的節省了cpu 和內存

          系統的工作流程大概是:每個客戶端連接成功后,系統會分配一個goroutine讀取客戶端的消息,當消息讀取完成,將它轉化為消息對象放至在SET的接收消息隊列,然后返回獲取下一個消息。

          在SET內部,有一個工作goroutine,它只做非常簡單而高效的事情,它做的事情如下,檢查SET的接受消息,它會收到3類消息


          1. 客戶端的搖紅包請求消息
          2. 客戶端的其他消息 比如聊天 好友這一類
          3. 服務器端對客戶端消息的回應


          對于第1種消息客戶端的搖紅包請求消息 是這樣處理的,從客戶端拿到搖紅包請求消息,試圖從SET的紅包隊列里 獲取一個紅包,如果拿到了就把紅包信息 返回給客戶端,否則構造一個沒有搖到的消息,返回給對應的客戶端。

          對于第2種消息客戶端的其他消息 比如聊天 好友這一類,只需簡單地從隊列里拿走消息,轉發給后端的聊天服務隊列即可,其他服務會把消息轉發出去。

          對于第3種消息服務器端對客戶端消息的回應。SET 只需要根據消息里的用戶id,找到SET里保留的用戶連接對象,發回去就可以了。

          對于紅包產生服務,它的工作很簡單,只需要按照順序在輪流在每個SET的紅包產生對列里放至紅包對象就可以了。這樣可以保證每個SET里都是公平的,其次它的工作強度很低,可以保證業務穩定。

          見代碼:

          https://github.com/xiaojiaqi/10billionhongbaos


          7. 實踐

          實踐的過程分為3個階段

          階段1

          分別啟動服務器端和監控端,然后逐一啟動17臺客戶端,讓它們建立起100萬的鏈接。在服務器端,利用ss 命令 統計出每個客戶端和服務器建立了多少連接。

          命令如下:


           Alias ss2=Ss –ant | grep 1025 | grep EST | awk –F: “{print \$8}” | sort | uniq –c’
          

          結果如下:


          階段2

          利用客戶端的http接口,將所有的客戶端QPS 調整到3萬,讓客戶端發出3W QPS強度的請求。

          運行如下命令:

          觀察網絡監控和監控端反饋,發現QPS 達到預期數據,網絡監控截圖:

          在服務器端啟動一個產生紅包的服務,這個服務會以200個每秒的速度下發紅包,總共4萬個。此時觀察客戶端在監控上的日志,會發現基本上以200個每秒的速度獲取到紅包。

          等到所有紅包下發完成后,再啟動一個發紅包的服務,這個服務系統會生成2萬個紅包,每秒也是200個,每個紅包隨機指定3位用戶,并向這3個用戶發出消息,客戶端會自動來拿紅包,最后所有的紅包都被拿走。


          階段3

          利用客戶端的http接口,將所有的客戶端QPS 調整到6萬,讓客戶端發出6W QPS強度的請求。

          如法炮制,在服務器端,啟動一個產生紅包的服務,這個服務會以200個每秒的速度下發紅包??偣?萬個。此時觀察客戶端在監控上的日志,會發現基本上以200個每秒的速度獲取到紅包。

          等到所有紅包下發完成后,再啟動一個發紅包的服務,這個服務系統會生成2萬個紅包,每秒也是200個,每個紅包隨機指定3位用戶,并向這3個用戶發出消息,客戶端會自動來拿紅包,最后所有的紅包都被拿走。

          最后,實踐完成。

          8. 分析數據

          在實踐過程中,服務器和客戶端都將自己內部的計數器記錄發往監控端,成為了日志。我們利用簡單python 腳本和gnuplt 繪圖工具,將實踐的過程可視化,由此來驗證運行過程。

          第一張是客戶端的QPS發送數據:

          這張圖的橫坐標是時間,單位是秒,縱坐標是QPS,表示這時刻所有客戶端發送的請求的QPS。

          圖的第一區間,幾個小的峰值,是100萬客戶端建立連接的, 圖的第二區間是3萬QPS 區間,我們可以看到數據 比較穩定的保持在3萬這個區間。最后是6萬QPS區間。但是從整張圖可以看到QPS不是完美地保持在我們希望的直線上。這主要是以下幾個原因造成的


          1. 當非常多goroutine 同時運行的時候,依靠sleep 定時并不準確,發生了偏移。我覺得這是golang本身調度導致的。當然如果cpu比較強勁,這個現象會消失。
          2. 因為網絡的影響,客戶端在發起連接時,可能發生延遲,導致在前1秒沒有完成連接。
          3. 服務器負載較大時,1000M網絡已經出現了丟包現象,可以通過ifconfig 命令觀察到這個現象,所以會有QPS的波動。


          第二張是 服務器處理的QPS圖:

          和客戶端的向對應的,服務器也存在3個區間,和客戶端的情況很接近。但是我們看到了在大概22:57分,系統的處理能力就有一個明顯的下降,隨后又提高的尖狀。這說明代碼還需要優化。

          整體觀察在3萬QPS區間,服務器的QPS比較穩定,在6萬QSP時候,服務器的處理就不穩定了。我相信這和我的代碼有關,如果繼續優化的話,還應該能有更好的效果。

          將2張圖合并起來 :

          基本是吻合的,這也證明系統是符合預期設計的。

          這是紅包生成數量的狀態變化圖:

          非常的穩定。

          這是客戶端每秒獲取的搖紅包狀態:

          可以發現3萬QPS區間,客戶端每秒獲取的紅包數基本在200左右,在6萬QPS的時候,以及出現劇烈的抖動,不能保證在200這個數值了。我覺得主要是6萬QPS時候,網絡的抖動加劇了,造成了紅包數目也在抖動。

          最后是golang 自帶的pprof 信息,其中有gc 時間超過了10ms, 考慮到這是一個7年前的硬件,而且非獨占模式,所以還是可以接受。


          總結

          按照設計目標,我們模擬和設計了一個支持100萬用戶,并且每秒至少可以支持3萬QPS,最多6萬QPS的系統,簡單模擬了微信的搖紅包和發紅包的過程??梢哉f達到了預期的目的。

          如果600臺主機每臺主機可以支持6萬QPS,只需要7分鐘就可以完成 100億次搖紅包請求。

          雖然這個原型簡單地完成了預設的業務,但是它和真正的服務會有哪些差別呢?我羅列了一下


          Refers:


          • 單機百萬的實踐
          • https://github.com/xiaojiaqi/C1000kPracticeGuide
          • 如何在AWS上進行100萬用戶壓力測試
          • https://github.com/xiaojiaqi/fakewechat/wiki/Stress-Testing-in-the-Cloud
          • 構建一個你自己的類微信系統
          • https://github.com/xiaojiaqi/fakewechat/wiki/Design
          • http://djt.qq.com/article/view/1356
          • http://techblog.cloudperf.net/2016/05/2-million-packets-per-second-on-public.html
          • http://datacratic.com/site/blog/1m-qps-nginx-and-ubuntu-1204-ec2
          • @火丁筆記
          • http://huoding.com/2013/10/30/296
          • https://gobyexample.com/non-blocking-channel-operations

          項目地址:https://github.com/xiaojiaqi/10billionhongbaos

          時音視頻的開發學習有很多可以參考的開源項目。

          音視頻流媒體在現在的生活中已經無處不在,擁有一大批頂級的音頻/視頻工具確實派得上用場。修剪文件、編輯視頻、最大化音頻――我們需要滿足社交媒體流的傳播需求,而公司總是需要音頻/視頻內容,以便與用戶進行最有效的溝通。

          一個實時音視頻應用共包括幾個環節:采集、編碼、前后處理、傳輸、解碼、緩沖、渲染等很多環節。每一個細分環節,還有更細分的技術模塊。比如,前后處理環節有美顏、濾鏡、回聲消除、噪聲抑制等,采集有麥克風陣列等,編解碼有VP8、VP9、H.264、H.265等。

          我們今天匯總了一些能幫助到正在學習或進行音視頻開發的實時音視頻開發者們的開源項目與幾個也在為開源社區貢獻力量的商業服務。這些項目分為幾類:音視頻編解碼類、視頻前后處理、服務端類等。

          音視頻編解碼類開源項目

          視頻編解碼的作用,就是在設備的攝像頭采集畫面和前處理后,將圖像進行壓縮,進行數字編碼,用于傳輸。編解碼器的優劣基本在于:壓縮效率的高低,速度和功耗。

          目前,主流的視頻編碼器分為3個系列:VPx(VP8,VP9),H.26x(H.264,H.265),AVS(AVS1.0,AVS2.0)。VPx系列是由Google開源的視頻編解碼標準。在保證相同質量情況下,VP9相比VP8碼率減少約50%。H.26x系列在硬件支持上比較廣泛,H.265的編碼效率能比上一代提高了30-50%,但是復雜度和功耗會比上一代大很多,所以純軟件編碼實現的話有一定瓶頸,現有的技術下,還是需要依靠硬件編解碼為主。AVS是我國具備自主知識產權的第二代信源編碼標準,目前已經發展到第二代。

          WebRTC

          首先會用到的肯定是WebRTC,是一個支持網頁瀏覽器進行實時語音對話或視頻對話的開源項目。它提供了包括音視頻的采集、編解碼、網絡傳輸、顯示等功能。如果你想基于WebRTC開發實時音視頻應用,需要注意,由于WebRTC缺少服務端設計和部署方案,你還需要將WebRTC與Janus等服務端類開源項目結合即可。

          官網地址: ?webrtc.org/?

          x264

          H.264是目前應用最廣的碼流標準。x264則是能夠產生符合H.264標準的碼流的編碼器,它可以將視頻流編碼為H.264、MPEG-4 AVC格式。它提供了命令行接口與API,前者被用于一些圖形用戶接口例如Straxrip、MeGUI,后者則被FFmpeg、Handbrake等調用。當然,既然有x264,就有對應HEVC/H.265的x265。

          官網地址:? ?https://www.videolan.org/developers/x264.html?

          FFmpeg

          FFmpeg大家應該不陌生,提供了編碼、解碼、轉換、封裝等功能,以及剪裁、縮放、色域等后期處理,支持幾乎目前所有音視頻編碼標準(由于格式眾多,我們就不一一列列舉了,可以在Wikipedia中找到)。

          同時,FFmpeg還衍生出了libav項目,從中誕生了視頻解碼器LAV,許多播放軟件都可調用LAV進行解碼,并且LAV本身也支持利用顯卡進行視頻硬解。很多主流視頻播放器中都以FFmpeg作為內核播放器。不僅僅是視頻播放器,就連Chrome這類可以播放網頁視頻的瀏覽器也受益于FFmpeg。很多開發者也基于FFmpeg做過很多開發并開源出來,比如大神雷霄驊(代碼可見他的sourceforge)。

          官網地址:? ?ffmpeg.org/?

          ijkplayer

          在介紹ijkplayer之前,要先提到ffplay。ffplay是一個使用了FFmpeg和sdl庫的可移植的媒體播放器。ijkplay是Bilibili開源的基于ffplay.c實現的輕量級iOS/Android視頻播放器,API易于集成,且編譯配置可裁剪,利于控制安裝包大小。

          在編解碼方面,ijkplayer支持視頻軟解和硬解,可以在播放前配置,但在播放過程中則不能切換。iOS和Android上視頻硬解可分別使用大家熟悉的VideoToolbox和MediaCodec。但ijkplayer對音頻僅支持軟解。

          Github地址:? ?https://github.com/Bilibili/ijkplayer?

          JSMpeg

          JSMpeg是一個基于JavaScript的MPEG1視頻的解碼器。如果要做H5端的視頻直播,可以考慮使用JSMpeg在移動端進行解碼。在H5端做音視頻直播,可以使用JSMpeg進行視頻解碼,這也是最近比較火的H5抓娃娃的主流策略。

          Github地址:? ?https://github.com/phoboslab/jsmpeg?

          Opus

          Opus是用C語言開發的一個高靈活度的音頻編碼器,針對ARM、x86有特殊優化,fix-point實現。Opus在各方面都有著明顯優勢。它同時支持語音與音樂的編碼,比特率為6k-510k。它融合了SILK編碼方法和CELT編碼方法。SILK原本被用于Skype中,基于語音信號的線性預測分析(LPC),對音樂支持并不好。而CELT盡管適用于全帶寬音頻,但對低比特率語音的編碼效率不高,所以兩者在Opus中形成了互補。

          Opus是“取代”了Speex。但是Speex中有的功能,Opus卻沒有,比如回聲消除。這個功能已經從編碼器中獨立出來。所以如果想實現好的回聲消除,可以配合WebRTC的AEC和AECM模塊做二次開發。

          官網地址:? ?opus-codec.org/?

          live555

          live555是一個C++流媒體開源項目,其中不僅包括了傳輸協議(SIP、RTP)、音視頻編碼器(H.264、MPEG4)等,還包括流媒體服務器的例子,是流媒體項目的首選,里面的傳輸模塊是非常值得視頻會議開發作為參考的。

          官網地址:? ?www.live555.com/?

          本文福利, 免費領取C++音視頻學習資料包+學習路線大綱、技術視頻/代碼,內容包括(音視頻開發,面試題,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,編解碼,推拉流,srs),有需要的可以進企鵝裙927239107領取哦~

          音視頻前后處理開源項目

          前后處理包含很多細分技術,應用正確的話,對視頻質量或多或少都有提升。不過每增加一個處理環節,必然會增加運算量與延時,所以如何取舍,還要大家各自斟酌。

          Seetaface

          Seetaface是由中科院山世光老師開源的一套完整的人臉檢測,人臉對齊和人臉驗證方案。代碼基于C++實現,開源協議為BSD-2,可供學術界和工業界免費使用。且不依賴于任何第三方的庫函數,在使用對齊好的LFW圖片上,檢測對齊全部使用該開源軟件的情況下可達到97.1%。

          Github地址:? ?https://github.com/seetaface/SeetaFaceEngine?

          GPUImage

          現在在iOS端做美顏效果、加水印,基本都會采用GPUImage,它內置了125種渲染效果, 還支持腳本自定義。該項目實現了圖片濾鏡、攝像頭實時濾鏡。它優勢在于處理效果是基于GPU實現,相對于CPU處理性能更高。

          Github地址:? ?https://github.com/BradLarson/GPUImage?

          Open nsfw model

          Open nsfw model是雅虎開源項目,全名是Open Not suitable for work model,專門鑒別不適合工作時間瀏覽的圖片(言而言之就是小黃圖)。它是基于Caffe框架訓練的模型,用于音視頻后處理。不過,它還不能鑒別恐怖、血腥圖片。

          Github地址:? ?https://github.com/yahoo/open_nsfw??

          Soundtouch

          Soundtouch是一個開源的音頻處理框架,主要功能對音頻變速、變調,實現變聲的效果。同時,它也能對媒體流實時處理。采用32位浮點或者16位定點,支持單聲道或者雙聲道,采樣率范圍為8k - 48k。

          官網地址:? ?www.surina.net/soundtouch/??

          服務端類開源項目

          正如開始時我們所說,WebRTC缺少服務端的設計與部署,利用MCU、SFU實現多人聊天,提高傳輸質量,都需要開發者自己動手。而下面這些開源項目能夠幫到你。

          Jitsi

          Jitsi是開源的視頻會議系統,可以實現在線視頻會議,文檔共享和即時消息的分享。它支持網絡視頻會議,使用SFU模式實現視頻路由器功能。開發語言是Java。它支持SIP帳號注冊電話呼叫。不僅支持單機本地安裝方式,還支持云平臺安裝。

          官網地址:? ?jitsi.org/??

          JsSIP

          JsSIP是基于WebRTC的JavaScript SIP協議實現的庫,可以在瀏覽器和Node.js中運行。它可以與 OverSIP、Kamailio、Asterisk、OfficeSIP等SIP Server一起運行。

          Github地址:?https://github.com/versatica/JsSIP

          SRS

          SRS是一個采用MIT協議授權的國產的簡單的RTMP/HLS 直播服務器。最新版還支持FLV模式,同時具備了RTMP的實時性,以及HLS中屬于HTTP協議對各種網絡環境高度適應性,并且支持更多播放器。它的功能與nginx-rtmp-module類似, 可以實現RTMP/HLS的分發。

          Github地址:? ?https://github.com/ossrs/srs

          JRTPLIB

          JRTPLIB 是一個開源的 RTP協議實現庫,支持Windows和unix平臺。它支持多線程,處理性能較好。它還支持RFC3550、UDP IPV6,支持自定義擴展傳輸協議。但它不支持TCP傳輸,這需要開發者自己來實現。同時,它也不支持音視頻的分包,代碼要你自己來實現。

          Github地址:? ?https://github.com/j0r1/JRTPLIB

          OPAL

          OPAL是OpenH323的下一個版本,繼承了Openh323協議,其新包含了SIP協議棧,是實現SIP協議的首選,缺點是參考例子較少。

          代碼地址:? ?https://link.zhihu.com/?target=http://sourceforge.net/projects/opalvoip/files/

          Kurento

          Kurento是一個基于WebRTC的媒體服務端,并包含了一系列API,可以簡化web與移動端實時視頻應用的開發。

          Github地址:? ?https://github.com/Kurento

          Janus

          Janus是一個WebRTC媒體網關。不論是做流媒體、視頻會議、錄制、網關,都可以基于Janus來實現。

          Github地址:github.com/Kurento

          ? ?Callstats.io??

          實時通信過程中的,延時、丟包、接通率、掉線率等質量問題,都影響用戶體驗。商用項目尤其需要關注。Callstats是一家通過對WebRTC呼叫進行專業監測,來幫助用戶搜集通訊數據,提升通話質量的服務商。

          Callstats也通過Github開放很多案例,可供使用Jitsi-videobridge,、turn-server、JsSIP的開發者參考。

          Github地址:?https://github.com/callstats-io

          Meetecho

          Meetecho是著名的開源WebRTC網關項目Janus的開發者。他們還提供基于Janus開發的技術咨詢與部署服務、建立視頻會議直播與錄制服務等。

          Github地址:? ?https://github.com/carlhuda/janus

          聲網Agora

          聲網提供了從編解碼到端到端傳輸的全套服務,開發者可以接入上文所述的音視頻前后處理的開源項目,配合使用聲網SDK可以建立高質量的實時音視頻應用。在Web端,Agora Web SDK可以幫助WebRTC開發者解決服務端傳輸中會遇到的卡頓、延時、回聲、多人視頻不穩定等問題。同時,聲網SDK還對多個系統平臺的應用提供實時音視頻通訊服務。

          聲網在Github上有許多可供開發者參考、實踐的demo源碼,覆蓋了從網頁端、iOS到Android平臺,以及音視頻直播、游戲連麥、企業會議、AR、直播答題、小程序等多種實時互動應用場景。

          Github地址:? ?https://github.com/AgoraIO-Community

          我們在這里列出了18個開源項目,以及3個能有效保證實時音視頻傳輸質量的服務。不過篇幅有限,還有很多開源項目我們沒有詳細列出,比如在音視頻方面,http://Xiph.org的Speex、FLAC,還有Xvid、libvpx、Lagarith、Daala、Thor等。歡迎大家繼續補充。

          片來自包圖網

          知乎存儲平臺團隊基于開源 Redis 組件打造的 Redis 平臺管理系統,經過不斷的研發迭代,目前已經形成了一整套完整自動化運維服務體系,提供一鍵部署集群,一鍵自動擴縮容,Redis 超細粒度監控,旁路流量分析等輔助功能。

          目前,Redis 在知乎的規模如下:

          • 機器內存總量約 70TB,實際使用內存約 40TB。
          • 平均每秒處理約 1500 萬次請求,峰值每秒約 2000 萬次請求。
          • 每天處理約 1 萬億余次請求。
          • 單集群每秒處理最高每秒約 400 萬次請求。
          • 集群實例與單機實例總共約 800 個。
          • 實際運行約 16000 個 Redis 實例。
          • Redis 使用官方 3.0.7 版本,少部分實例采用 4.0.11 版本。

          知乎 Redis 平臺演進歷程

          根據業務的需求,我們將實例區分為如下兩種類型:

          • 單機(Standalone),單機實例通常用于容量與性能要求不高的小型存儲。
          • 集群(Cluster),集群則用來應對對性能和容量要求較高的場景。

          單機(Standalone)

          對于單機實例,我們采用原生主從(Master-Slave)模式實現高可用,常規模式下對外僅暴露 Master 節點。由于使用原生 Redis,所以單機實例支持所有 Redis 指令。

          對于單機實例,我們使用 Redis 自帶的哨兵(Sentinel)集群對實例進行狀態監控與 Failover。

          Sentinel 是 Redis 自帶的高可用組件,將 Redis 注冊到由多個 Sentinel 組成的 Sentinel 集群后,Sentinel 會對 Redis 實例進行健康檢查。

          當 Redis 發生故障后,Sentinel 會通過 Gossip 協議進行故障檢測,確認宕機后會通過一個簡化的 Raft 協議來提升 Slave 成為新的 Master。

          通常情況我們僅使用 1 個 Slave 節點進行冷備,如果有讀寫分離請求,可以建立多個 Read only slave 來進行讀寫分離。



          如上圖所示,通過向 Sentinel 集群注冊 Master 節點實現實例的高可用,當提交 Master 實例的連接信息后,Sentinel 會主動探測所有的 Slave 實例并建立連接,定期檢查健康狀態。

          客戶端通過多種資源發現策略如簡單的 DNS 發現 Master 節點,將來有計劃遷移到如 Consul 或 etcd 等資源發現組件 。

          當 Master 節點發生宕機時,Sentinel 集群會提升 Slave 節點為新的 Master,同時在自身的 pubsub channel +switch-master 廣播切換的消息,具體消息格式為:

          switch-master <master name> <oldip> <oldport> <newip> <newport> 
          

          Watcher 監聽到消息后,會去主動更新資源發現策略,將客戶端連接指向新的 Master 節點,完成 Failover。

          實際使用中需要注意以下幾點:

          • 只讀 Slave 節點可以按照需求設置 slave-priority 參數為 0,防止故障切換時選擇了只讀節點而不是熱備 Slave 節點。
          • Sentinel 進行故障切換后會執行 CONFIG REWRITE 命令將 SLAVEOF 配置落地,如果 Redis 配置中禁用了 CONFIG 命令,切換時會發生錯誤,可以通過修改 Sentinel 代碼來替換 CONFIG 命令。
          • Sentinel Group 監控的節點不宜過多,實測超過 500 個切換過程偶爾會進入 TILT 模式,導致 Sentinel 工作不正常,推薦部署多個 Sentinel 集群并保證每個集群監控的實例數量小于 300 個。
          • Master 節點應與 Slave 節點跨機器部署,有能力的使用方可以跨機架部署,不推薦跨機房部署 Redis 主從實例。
          • Sentinel 切換功能主要依賴 down-after-milliseconds 和failover-timeout 兩個參數,down-after-milliseconds 決定了Sentinel 判斷 Redis 節點宕機的超時,知乎使用 30000 作為閾值。

          而 failover-timeout 則決定了兩次切換之間的最短等待時間,如果對于切換成功率要求較高,可以適當縮短 failover-timeout 到秒級保證切換成功。

          • 單機網絡故障等同于機器宕機,但如果機房全網發生大規模故障會造成主從多次切換,此時資源發現服務可能更新不夠及時,需要人工介入。

          集群(Cluster)

          當實例需要的容量超過 20G 或要求的吞吐量超過 20 萬請求每秒時,我們會使用集群(Cluster)實例來承擔流量。

          集群是通過中間件(客戶端或中間代理等)將流量分散到多個 Redis 實例上的解決方案。

          知乎的 Redis 集群方案經歷了兩個階段:

          • 客戶端分片
          • Twemproxy 代理

          客戶端分片(before 2015)

          早期知乎使用 redis-shard 進行客戶端分片,redis-shard 庫內部實現了 CRC32、MD5、SHA1 三種哈希算法,支持絕大部分Redis 命令。使用者只需把 redis-shard 當成原生客戶端使用即可,無需關注底層分片。



          基于客戶端的分片模式具有如下優點:

          • 基于客戶端分片的方案是集群方案中最快的,沒有中間件,僅需要客戶端進行一次哈希計算,不需要經過代理,沒有官方集群方案的 MOVED/ASK 轉向。
          • 不需要多余的 Proxy 機器,不用考慮 Proxy 部署與維護。
          • 可以自定義更適合生產環境的哈希算法。

          但是也存在如下問題:

          • 需要每種語言都實現一遍客戶端邏輯,早期知乎全站使用 Python 進行開發,但是后來業務線增多,使用的語言增加至 Python,Golang,Lua,C/C++,JVM 系(Java,Scala,Kotlin)等,維護成本過高。
          • 無法正常使用 MSET、MGET 等多種同時操作多個 Key 的命令,需要使用 Hash tag 來保證多個 Key 在同一個分片上。
          • 升級麻煩,升級客戶端需要所有業務升級更新重啟,業務規模變大后無法推動。
          • 擴容困難,存儲需要停機使用腳本 Scan 所有的 Key 進行遷移,緩存只能通過傳統的翻倍取模方式進行擴容。
          • 由于每個客戶端都要與所有的分片建立池化連接,客戶端基數過大時會造成 Redis 端連接數過多,Redis 分片過多時會造成 Python 客戶端負載升高。
          • 早期知乎大部分業務由 Python 構建,Redis 使用的容量波動較小,redis-shard 很好地應對了這個時期的業務需求,在當時是一個較為不錯的解決方案。

          Twemproxy 集群 (2015 - Now)

          2015 年開始,業務上漲迅猛,Redis 需求暴增,原有的 redis-shard 模式已經無法滿足日益增長的擴容需求,我們開始調研多種集群方案,最終選擇了簡單高效的 Twemproxy 作為我們的集群方案。

          由 Twitter 開源的 Twemproxy 具有如下優點:

          • 性能很好且足夠穩定,自建內存池實現 Buffer 復用,代碼質量很高。
          • 支持 fnv1a_64、murmur、md5 等多種哈希算法。
          • 支持一致性哈希(ketama),取模哈希(modula)和隨機(random)三種分布式算法。

          但是缺點也很明顯:

          • 單核模型造成性能瓶頸。
          • 傳統擴容模式僅支持停機擴容。

          對此,我們將集群實例分成兩種模式:

          • 緩存(Cache)
          • 存儲(Storage)

          如果使用方可以接受通過損失一部分少量數據來保證可用性,或使用方可以從其余存儲恢復實例中的數據,這種實例即為緩存,其余情況均為存儲。我們對緩存和存儲采用了不同的策略。

          存儲



          對于存儲我們使用 fnv1a_64 算法結合 modula 模式即取模哈希對 Key 進行分片。

          底層 Redis 使用單機模式結合 Sentinel 集群實現高可用,默認使用 1 個 Master 節點和 1 個 Slave 節點提供服務,如果業務有更高的可用性要求,可以拓展 Slave 節點。

          當集群中 Master 節點宕機,按照單機模式下的高可用流程進行切換,Twemproxy 在連接斷開后會進行重連。

          對于存儲模式下的集群,我們不會設置 auto_eject_hosts,不會剔除節點。

          同時,對于存儲實例,我們默認使用 noeviction 策略,在內存使用超過規定的額度時直接返回 OOM 錯誤,不會主動進行 Key 的刪除,保證數據的完整性。

          由于 Twemproxy 僅進行高性能的命令轉發,不進行讀寫分離,所以默認沒有讀寫分離功能。

          而在實際使用過程中,我們也沒有遇到集群讀寫分離的需求,如果要進行讀寫分離,可以使用資源發現策略在 Slave 節點上架設 Twemproxy 集群,由客戶端進行讀寫分離的路由。

          緩存

          考慮到對于后端(MySQL/HBase/RPC 等)的壓力,知乎絕大部分業務都沒有針對緩存進行降級,這種情況下對緩存的可用性要求較數據的一致性要求更高。

          但是如果按照存儲的主從模式實現高可用,1 個 Slave 節點的部署策略在線上環境只能容忍 1 臺物理節點宕機,N 臺物理節點宕機高可用就需要至少 N 個 Slave 節點,這無疑是種資源的浪費。



          所以我們采用了 Twemproxy 一致性哈希(Consistent Hashing)策略來配合 auto_eject_hosts 自動彈出策略組建 Redis 緩存集群。

          對于緩存我們仍然使用 fnv1a_64 算法進行哈希計算,但是分布算法我們使用了 ketama 即一致性哈希進行 Key 分布。緩存節點沒有主從,每個分片僅有 1 個 Master 節點承載流量。

          Twemproxy 配置 auto_eject_hosts 會在實例連接失敗超過server_failure_limit 次的情況下剔除節點。

          并在 server_retry_timeout 超時之后進行重試,剔除后配合 ketama 一致性哈希算法重新計算哈希環,恢復正常使用,這樣即使一次宕機多個物理節點仍然能保持服務。



          在實際的生產環境中需要注意以下幾點:

          • 剔除節點后,會造成短時間的命中率下降,后端存儲如 MySQL、HBase 等需要做好流量監測。
          • 線上環境緩存后端分片不宜過大,建議維持在 20G 以內,同時分片調度應盡可能分散,這樣即使宕機一部分節點,對后端造成的額外的壓力也不會太多。
          • 機器宕機重啟后,緩存實例需要清空數據之后啟動,否則原有的緩存數據和新建立的緩存數據會沖突導致臟緩存。

          直接不啟動緩存也是一種方法,但是在分片宕機期間會導致周期性 server_failure_limit 次數的連接失敗。

          • server_retry_timeout 和 server_failure_limit 需要仔細敲定確認,知乎使用 10min 和 3 次作為配置,即連接失敗 3 次后剔除節點,10 分鐘后重新進行連接。

          Twemproxy 部署

          在方案早期我們使用數量固定的物理機部署 Twemproxy,通過物理機上的 Agent 啟動實例,Agent 在運行期間會對 Twemproxy 進行健康檢查與故障恢復。

          由于 Twemproxy 僅提供全量的使用計數,所以 Agent 運行時還會進行定時的差值計算來計算 Twemproxy 的 requests_per_second 等指標。

          后來為了更好地故障檢測和資源調度,我們引入了 Kubernetes,將 Twemproxy 和 Agent 放入同一個 Pod 的兩個容器內,底層 Docker 網段的配置使每個 Pod 都能獲得獨立的 IP,方便管理。

          最開始,本著簡單易用的原則,我們使用 DNS A Record 來進行客戶端的資源發現,每個 Twemproxy 采用相同的端口號,一個 DNS A Record 后面掛接多個 IP 地址對應多個 Twemproxy 實例。

          初期,這種方案簡單易用,但是到了后期流量日益上漲,單集群 Twemproxy 實例個數很快就超過了 20 個。

          由于 DNS 采用的 UDP 協議有 512 字節的包大小限制,單個 A Record 只能掛接 20 個左右的 IP 地址,超過這個數字就會轉換為 TCP 協議,客戶端不做處理就會報錯,導致客戶端啟動失敗。

          當時由于情況緊急,只能建立多個 Twemproxy Group,提供多個 DNS A Record 給客戶端,客戶端進行輪詢或者隨機選擇,該方案可用,但是不夠優雅。

          如何解決 Twemproxy 單 CPU 計算能力的限制?

          之后我們修改了 Twemproxy 源碼, 加入 SO_REUSEPORT 支持。



          Twemproxy with SO_REUSEPORT on Kubernetes

          同一個容器內由 Starter 啟動多個 Twemproxy 實例并綁定到同一個端口,由操作系統進行負載均衡,對外仍然暴露一個端口,但是內部已經由系統均攤到了多個 Twemproxy 上。

          同時 Starter 會定時去每個 Twemproxy 的 stats 端口獲取 Twemproxy 運行狀態進行聚合,此外 Starter 還承載了信號轉發的職責。

          原有的Agent 不需要用來啟動 Twemproxy 實例,所以 Monitor 調用 Starter 獲取聚合后的 stats 信息進行差值計算,最終對外界暴露出實時的運行狀態信息。

          為什么沒有使用官方 Redis 集群方案?

          我們在 2015 年調研過多種集群方案,綜合評估多種方案后,最終選擇了看起來較為陳舊的 Twemproxy 而不是官方 Redis 集群方案與 Codis,具體原因如下:

          MIGRATE 造成的阻塞問題:Redis 官方集群方案使用 CRC16 算法計算哈希值并將 Key 分散到 16384 個 Slot 中,由使用方自行分配 Slot 對應到每個分片中。

          擴容時由使用方自行選擇 Slot 并對其進行遍歷,對 Slot 中每一個 Key 執行 MIGRATE 命令進行遷移。

          調研后發現,MIGRATE 命令實現分為三個階段:

          • DUMP 階段:由源實例遍歷對應 Key 的內存空間,將 Key 對應的 Redis Object 序列化,序列化協議跟 Redis RDB 過程一致。
          • RESTORE 階段:由源實例建立 TCP 連接到對端實例,并將 DUMP 出來的內容使用 RESTORE 命令到對端進行重建,新版本的 Redis 會緩存對端實例的連接。
          • DEL 階段(可選):如果發生遷移失敗,可能會造成同名的 Key 同時存在于兩個節點。

          此時 MIGRATE 的 REPLACE 參數決定是否覆蓋對端的同名 Key,如果覆蓋,對端的 Key 會進行一次刪除操作,4.0 版本之后刪除可以異步進行,不會阻塞主進程。

          經過調研,我們認為這種模式并不適合知乎的生產環境。Redis 為了保證遷移的一致性, MIGRATE 所有操作都是同步操作,執行 MIGRATE 時,兩端的 Redis 均會進入時長不等的 BLOCK 狀態。

          對于小 Key,該時間可以忽略不計,但如果一旦 Key 的內存使用過大,一個 MIGRATE 命令輕則導致 P95 尖刺,重則直接觸發集群內的 Failover,造成不必要的切換。

          同時,遷移過程中訪問到處于遷移中間狀態的Slot 的 Key 時,根據進度可能會產生 ASK 轉向,此時需要客戶端發送 ASKING 命令到 Slot 所在的另一個分片重新請求,請求時延則會變為原來的兩倍。

          同樣,方案初期時的 Codis 采用的是相同的 MIGRATE 方案,但是使用 Proxy 控制 Redis 進行遷移操作而非第三方腳本(如 redis-trib.rb),基于同步的類似 MIGRATE 的命令,實際跟 Redis 官方集群方案存在同樣的問題。

          對于這種 Huge Key 問題決定權完全在于業務方,有時業務需要不得不產生 Huge Key 時會十分尷尬,如關注列表。

          一旦業務使用不當出現超過 1MB 以上的大 Key 便會導致數十毫秒的延遲,遠高于平時 Redis 亞毫秒級的延遲。

          有時,在 Slot 遷移過程中業務不慎同時寫入了多個巨大的 Key 到 Slot 遷移的源節點和目標節點,除非寫腳本刪除這些 Key ,否則遷移會進入進退兩難的地步。

          對此,Redis 作者在 Redis 4.2 的 roadmap[5] 中提到了 Non blocking MIGRATE。

          但是截至目前,Redis 5.0 即將正式發布,仍未看到有關改動,社區中已經有相關的 Pull Request [6],該功能可能會在 5.2 或者 6.0 之后并入 Master 分支,對此我們將持續觀望。

          緩存模式下高可用方案不夠靈活:還有,官方集群方案的高可用策略僅有主從一種,高可用級別跟 Slave 的數量成正相關。

          如果只有一個 Slave,則只能允許一臺物理機器宕機,Redis 4.2 roadmap 提到了 cache-only mode,提供類似于 Twemproxy 的自動剔除后重分片策略,但是截至目前仍未實現。

          內置 Sentinel 造成額外流量負載:另外,官方 Redis 集群方案將 Sentinel 功能內置到 Redis 內,這導致在節點數較多(大于 100)時在 Gossip 階段會產生大量的 PING/INFO/CLUSTER INFO 流量。

          根據 issue 中提到的情況,200 個使用 3.2.8 版本節點搭建的 Redis 集群,在沒有任何客戶端請求的情況下,每個節點仍然會產生 40Mb/s 的流量。

          雖然到后期 Redis 官方嘗試對其進行壓縮修復,但按照 Redis 集群機制,節點較多的情況下無論如何都會產生這部分流量,對于使用大內存機器但是使用千兆網卡的用戶這是一個值得注意的地方。

          Slot 存儲開銷:最后,每個Key 對應的 Slot 的存儲開銷,在規模較大的時候會占用較多內存,4.x 版本以前甚至會達到實際使用內存的數倍。

          雖然 4.x 版本使用 rax 結構進行存儲,但是仍然占據了大量內存,從非官方集群方案遷移到官方集群方案時,需要注意這部分多出來的內存。

          總之,官方 Redis 集群方案與 Codis 方案對于絕大多數場景來說都是非常優秀的解決方案。

          但是我們仔細調研發現并不是很適合集群數量較多且使用方式多樣化的我們,場景不同側重點也會不一樣,但在此仍然要感謝開發這些組件的開發者們,感謝你們對 Redis 社區的貢獻。

          擴容

          靜態擴容

          對于單機實例,如果通過調度器觀察到對應的機器仍然有空閑的內存,我們僅需直接調整實例的 maxmemory 配置與報警即可。

          同樣,對于集群實例,我們通過調度器觀察每個節點所在的機器,如果所有節點所在機器均有空閑內存,我們會像擴容單機實例一樣直接更新 maxmemory 與報警。

          動態擴容

          但是當機器空閑內存不夠,或單機實例與集群的后端實例過大時,無法直接擴容,需要進行動態擴容:

          • 對于單機實例,如果單實例超過 30GB 且沒有如 sinterstore 之類的多 Key 操作,我們會將其擴容為集群實例。
          • 對于集群實例,我們會進行橫向的重分片,我們稱之為 Resharding 過程。



          Resharding 過程

          原生 Twemproxy 集群方案并不支持擴容,我們開發了數據遷移工具來進行 Twemproxy 的擴容,遷移工具本質上是一個上下游之間的代理,將數據從上游按照新的分片方式搬運到下游。

          原生 Redis 主從同步使用 SYNC/PSYNC 命令建立主從連接,收到 SYNC 命令的 Master 會 fork 出一個進程遍歷內存空間生成 RDB 文件并發送給 Slave。

          期間所有發送至 Master 的寫命令在執行的同時都會被緩存到內存的緩沖區內,當 RDB 發送完成后,Master 會將緩沖區內的命令及之后的寫命令轉發給 Slave 節點。

          我們開發的遷移代理會向上游發送 SYNC 命令模擬上游實例的 Slave,代理收到 RDB 后進行解析。

          由于 RDB 中每個 Key 的格式與 RESTORE 命令的格式相同,所以我們使用生成 RESTORE 命令按照下游的 Key 重新計算哈希并使用 Pipeline 批量發送給下游。

          等待 RDB 轉發完成后,我們按照新的后端生成新的 Twemproxy 配置,并按照新的 Twemproxy 配置建立 Canary 實例。

          從上游的 Redis 后端中取 Key 進行測試,測試 Resharding 過程是否正確,測試過程中的 Key 按照大小,類型,TTL 進行比較。

          測試通過后,對于集群實例,我們使用生成好的配置替代原有 Twemproxy 配置并 restart/reload Twemproxy 代理。

          我們修改了 Twemproxy 代碼,加入了 config reload 功能,但是實際使用中發現直接重啟實例更加可控。

          而對于單機實例,由于單機實例和集群實例對于命令的支持不同,通常需要和業務方確定后手動重啟切換。

          由于 Twemproxy 部署于 Kubernetes ,我們可以實現細粒度的灰度,如果客戶端接入了讀寫分離,我們可以先將讀流量接入新集群,最終接入全部流量。

          這樣相對于 Redis 官方集群方案,除在上游進行 BGSAVE 時的 fork 復制頁表時造成的尖刺以及重啟時造成的連接閃斷,其余對于 Redis 上游造成的影響微乎其微。

          這樣擴容存在的問題:

          對上游發送 SYNC 后,上游fork 時會造成尖刺:對于存儲實例,我們使用Slave 進行數據同步,不會影響到接收請求的 Master 節點。

          對于緩存實例,由于沒有 Slave 實例,該尖刺無法避免,如果對于尖刺過于敏感,我們可以跳過 RDB 階段,直接通過 PSYNC 使用最新的 SET 消息建立下游的緩存。

          切換過程中有可能寫到下游,而讀在上游:對于接入了讀寫分離的客戶端,我們會先切換讀流量到下游實例,再切換寫流量。

          一致性問題:兩條具有先后順序的寫同一個 Key 命令在切換代理后端時會通過 1)寫上游同步到下游 2)直接寫到下游兩種方式寫到下游。

          此時,可能存在應先執行的命令卻通過 1)執行落后于通過 2)執行,導致命令先后順序倒置。

          這個問題在切換過程中無法避免,好在絕大部分應用沒有這種問題,如果無法接受,只能通過上游停寫排空 Resharding 代理保證先后順序。

          官方 Redis 集群方案和 Codis 會通過 blocking 的 MIGRATE 命令來保證一致性,不存在這種問題。

          實際使用過程中,如果上游分片安排合理,可實現數千萬次每秒的遷移速度,1TB 的實例 Resharding 只需要半小時左右。

          另外,對于實際生產環境來說,提前做好預期規劃比遇到問題緊急擴容要快且安全得多。

          旁路分析

          由于生產環境調試需要,有時會需要監控線上 Redis 實例的訪問情況,Redis 提供了多種監控手段,如 MONITOR 命令。

          但由于 Redis 單線程的限制,導致自帶的 MONITOR 命令在負載過高的情況下會再次跑高 CPU,對于生產環境來說過于危險。

          而其余方式如 Keyspace Notify 只有寫事件,沒有讀事件,無法做到細致的觀察。

          對此我們開發了基于 libpcap 的旁路分析工具,系統層面復制流量,對應用層流量進行協議分析,實現旁路 MONITOR,實測對于運行中的實例影響微乎其微。

          同時對于沒有 MONITOR 命令的 Twemproxy,旁路分析工具仍能進行分析。

          由于生產環境中絕大部分業務都使用 Kubernetes 部署于 Docker 內 ,每個容器都有對應的獨立 IP。

          所以可以使用旁路分析工具反向解析找出客戶端所在的應用,分析業務方的使用模式,防止不正常的使用。

          將來的工作

          由于 Redis 5.0 發布在即,4.0 版本趨于穩定,我們將逐步升級實例到 4.0 版本,由此帶來的如 MEMORY 命令、Redis Module 、新的 LFU 算法等特性無論對運維方還是業務方都有極大的幫助。

          最后

          知乎架構平臺團隊是支撐整個知乎業務的基礎技術團隊,開發和維護著知乎幾乎全量的核心基礎組件。

          包括容器、Redis、MySQL、Kafka、LB、HBase 等核心基礎設施,團隊小而精,每個同學都獨當一面負責上面提到的某個核心系統。

          隨著知乎業務規模的快速增長,以及業務復雜度的持續增加,團隊面臨的技術挑戰也越來越大。

          參考資料:

          • Redis Official site

          https://redis.io/

          • Twemproxy Github Page

          https://github.com/twitter/twemproxy

          • Codis Github Page

          https://github.com/CodisLabs/codis

          • SO_REUSEPORT Man Page

          http://man7.org/linux/man-pages/man7/socket.7.html

          • Kubernetes

          https://kubernetes.io/

          作者:陳鵬

          簡介:現知乎存儲平臺組 Redis 平臺技術負責人,2014 年加入知乎技術平臺組從事基礎架構相關系統的開發與運維,從無到有建立了知乎 Redis 平臺,承載了知乎高速增長的業務流量。


          主站蜘蛛池模板: 精品无码av一区二区三区 | 亚洲av午夜福利精品一区| 国产精品区AV一区二区| 丰满爆乳无码一区二区三区| 97久久精品一区二区三区| 红桃AV一区二区三区在线无码AV | 日韩免费无码一区二区三区 | 亚洲日本久久一区二区va| 亚洲国产精品一区二区九九| 日本精品一区二区在线播放| 一区二区传媒有限公司| 色偷偷av一区二区三区| 一区二区三区亚洲| 91久久精品午夜一区二区| 亚洲av无码一区二区三区不卡| 亚洲一区二区视频在线观看| 一区二区三区在线观看免费| 日韩一区二区三区在线观看| 视频在线观看一区二区| 一区二区三区美女视频| 国产激情一区二区三区 | 久久国产午夜精品一区二区三区| 成人国产精品一区二区网站| 国产精品自在拍一区二区不卡| 精品一区二区三区在线观看| 亚洲日本中文字幕一区二区三区| 一区二区三区免费视频网站| 国产激情一区二区三区| 国精无码欧精品亚洲一区| 精品久久一区二区三区| 无码精品视频一区二区三区| 少妇精品久久久一区二区三区| 骚片AV蜜桃精品一区| 国产精品一区视频| 精品一区二区三区在线播放| 国产精品男男视频一区二区三区 | 亚洲一区二区三区四区视频| 国产精品视频分类一区| 日韩高清一区二区三区不卡 | 人妻aⅴ无码一区二区三区| 亚洲一区在线免费观看|