nginx-rtmp-module的缺陷分析

Arut最初在開發nginx-rtmp-module的時候只實現了單進程模式,好處是架構簡單,推送和播放,數據統計,流媒體控制等都在一個進程上完成。但是這顯然浪費了Nginx多進程(在Linux和FreeBSD平臺上每個進程都可以綁定一個CPU核心,以減少進程切換帶來的開銷)的處理能力。但是,如果開啓多進程模式,推送和播放如果不在同一個進程上,會造成播放失敗的問題:

另外,請求數據統計信息也是個問題,因爲採取HTTP方式請求數據統計信息時,在多進程模式下,請求被Nginx隨機分配給了worker進程,可能造成我想看worker 1上的數據統計信息,但是Nginx返回的是worker 3上的數據統計信息。流媒體控制也採取了HTTP請求的方式,所以也存在着同樣的問題:

針對推送和播放不在同一個進程上的問題,Arut後來加入了auto push的功能,即把原始的推流數據再relay到其他進程上去。這個功能需要Unix domain socket的支持(所以類Unix系統都支持,Windows在Windows 10的某個版本後纔開始支持):

這樣處理後,不管播放請求落在哪個進程上,都能獲得推送數據。

對於後面兩個問題,Arut給出了一個布丁,需要修改Nginx本身的源代碼,詳情見per-worker-listener。這樣處理後,在HTTP請求時加上端口號信息,就可以指定請求某個進程上的數據統計信息了,流媒體控制類似:

在測試中發現auto push的併發性能並不能隨着CPU個/核數的提高而提高,一般在400~500路後就無法再提升(筆記本測試)。是什麼原因呢?簡單分析一下:假設服務器的CPU個/核數爲N(Nginx的進程數一般配置爲跟CPU個/核數相等),推流路數爲M,且有M>>N,例如M=1000,N爲4。假設M個推流請求被平均分配到N個進程上(實際上是不會被絕對平均分配的,但是相差不會很大),那麼每個進程需要處理分配給自己本身的請求數爲:

Publishers_{self}=M/N

另外,要保證某個播放請求不管被哪個進程接受都能成功,那麼每個進程都要接受另外N-1個進程的auto push過來的流,即:

Publishers_{others}=M/N*(N-1)=M-M/N

那麼每個進程需要處理的推送路數爲:

Publishers_{all}=Publishers_{self}+Publishers_{others}=M/N+M-M/N=M

即每個進程需要處理的推流數跟CPU個/核數是沒有關係的,並不能用增加CPU個/核數來試圖提高推流併發性能,即相當於原本能將M個推流請求“平均”分配到N個進程上的方案不但沒起作用,還讓每個進程都處理了全部M個推流請求。

那麼怎麼解決這個問題呢?答案是使用被動拉的方案替代主動推(auto push)的方案。此方案要用到共享內存和互斥鎖,當一個推流請求被某個進程接受後,在共享內存中記錄推送的流和某個進程的映射信息。而當一個播放請求被某個進程接受後,需要先查找要播放的流是在哪個進程上發佈的,然後再到發佈流的進程上去請求數據:

這樣就解決了上述的每個進程都要處理全部進程接收到的推流請求的問題。

關於nginx-rtmp-module的缺陷暫時介紹到這兒,其實nginx-rtmp-module還有很多其他的缺陷,後續有時間我會寫文章介紹。

歡迎關注我在nginx-rtmp-module的基礎上開發的項目:nginx-http-flv-module。本項目已有多家商用案例。另外,歡迎關注我們的團隊:Car-eye-team和網站:liveoss流媒體平臺

其他文章:

基於nginx-rtmp-module模塊實現的HTTP-FLV直播模塊nginx-http-flv-module(一)

基於nginx-rtmp-module模塊實現的HTTP-FLV直播模塊nginx-http-flv-module(二)

基於nginx-rtmp-module模塊實現的HTTP-FLV直播模塊nginx-http-flv-module(三)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章