在實戰中使用nginx-rtmp遇到的TCP鏈接問題分析

在實戰中使用nginx-rtmp遇到的TCP鏈接問題分析

背景

前段時間公司作了一次體育賽事的現場直播,網絡由某通訊公司負責搭建,主要測試5G CPE上行網絡的帶寬和穩定性,爲了作到萬無一失,他們同時搭建了一條用做備份的400M光纖線路。經過配置交換機來作到主備切換,要達到如下的效果:html

  • 無線鏈路down掉,交換機自動檢測到丟包,丟包到指定數量(能夠在交換機中配置),自動切換到備用鏈路。
  • 無線連接恢復,備用鏈路切換回無線鏈路。

參考 靜態路由與SLA技術nginx

咱們採用nginx-rtmp搭建了2層CDN。git

測試

推流端推送RTMP流會向nginx-rtmp發送請求創建TCP連接,推流過程當中,把交換機上的無線鏈路網線拔掉。自動切換到光纖線路,推流端重連後依然不可以成功創建連接,推流軟件卡死。github

server端的TCP連接一直存在:網絡

root@iz2zehy7gff0ksipgb4ch3z /u/l/nginx# netstat -natp | grep "1936"
tcp        0      0 0.0.0.0:1936            0.0.0.0:*               LISTEN      9467/nginx: master  
tcp        0      0 192.168.199.6:1936      223.71.3.82:46012       ESTABLISHED 11177/nginx: worker

nginx 報錯了:app

2019/05/20 15:44:58 [error] 6947#0: *286 live: already publishing, client: 223.71.3.82, server: 0.0.0.0:1936

此時socket

就是由於無線連接斷開時,TCP連接不可以被正常關閉,publisher會一直存在致使的。tcp

複習一下四次揮手:ide

咱們知道TCP鏈接有一個特性:測試

TCP 鏈接一旦創建,只要通訊雙方之間的中間結點(包括網關和交換機、路由器等網絡設備)工做正常,那麼在通訊雙方中的任何一方主動關閉鏈接以前,TCP 鏈接都將被一直保持下去。TCP 鏈接的這種特性,使得一個長期不交換任何信息的空閒鏈接能夠長期保持數小時、數天甚至數月。中間路由器能夠崩潰、重啓,網線能夠被掛斷再連通,只要兩端的主機沒有被重啓,TCP 鏈接就能夠被一直保持下來。

能夠看到,網線雖然斷掉了,可是server端沒有收到client的任何消息,server端不會主動發起揮手,所以鏈接會一直維持很長一段時間(個人測試機器上大概數小時)。連接斷開後server端一直在發送PSH+ACK:

如何才能實現快速重連

爲源站加load balance

加一個備源和一個調度服務,調度策略採起輪詢,兩次連續的TCP鏈接請求會被定向到不一樣的源站上面。這個方法治標不治本,切一次能夠,若是無線鏈路恢復,再切回來的時候,可能TCP連接尚未關閉。

添加drop_idle_publisher

Syntax: drop_idle_publisher timeout
Context: rtmp, server, application

Drop publisher connection which has been idle (no audio/video data) within specified time. Default is off. Note this only works when connection is in publish mode (after sending publish command).

drop_idle_publisher 10s;

nginx-rtmp會在指定的時間內丟棄空閒的publisher:

root@iz2zehy7gff0ksipgb4ch3z /u/l/n/logs# netstat -natp | grep "1936"
tcp        0      0 0.0.0.0:1936            0.0.0.0:*               LISTEN      11421/nginx: master 
tcp        0      0 192.168.199.6:1936      61.148.243.150:9338     ESTABLISHED 12923/nginx: worker 
tcp        0      1 192.168.199.6:1936      223.71.3.82:47240       FIN_WAIT1   -

咱們將drop_idle_publisher設置爲2s,抓包可見此次是server端在2s後探測到這個TCP鏈接處於空閒狀態,主動發起了揮手消息,此時publisher就被釋放掉了,再次推流會從新創建新的TCP,從新生成此publisher。

上圖是鏈路斷掉後,TCP連接徹底斷開前server端向client發送的數據包,能夠看到一直在發送FIN+最後一個數據包的ACK,時間間隔大概爲 0.2秒->0.4秒->0.8秒->1.6秒->3.2秒->6.4秒->12.8秒->25.6秒

這種方法是可行的。

so_keepalive

listen

syntax: listen (addr[:port]|port|unix:path) [bind] [ipv6only=on|off] [so_keepalive=on|off|keepidle:keepintvl:keepcnt|proxy_protocol]

context: server

Adds listening socket to NGINX for accepting RTMP connections

關於TCP探活機制的幾個參數的說明:

  • keepcnt 關閉一個非活躍鏈接以前進行探測的最大次數t
  • keepidle 對一個鏈接進行有效性探測以前運行的最大非活躍時間間隔
  • keepintvl 兩個探測的時間間隔

設置以下參數:

listen 1936 so_keepalive=5s:2:2;

能夠看到,最後一個ACK沒有回覆後隔了5秒開始TCP keep-alive 探活,總共兩次,間隔2秒,最後發送RST+ACK斷開了TCP鏈接 。

參考

nginx-rtmp-module wiki

TCP 鏈接斷連問題剖析

相關文章
相關標籤/搜索