nginx-rtmp-module的缺陷分析

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

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

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

這樣處理後,無論播放請求落在哪一個進程上,都能得到推送數據。服務器

對於後面兩個問題,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 / Ndom

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

Publishers(others) = M / N x (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。

相關文章
相關標籤/搜索