用nginx作grpc反向代理,nginx到後端server不能維持長鏈接問題

問題描述

公司內部容器平臺,接入層用nginx作LB,用戶有grpc協議需求,因此在lb層支持grcp反向代理,nginx從1.13開始支持grpc反向代理,將公司使用的nginx包從1.12升級到1.14.0後,增長grpc反向代理配置。配置完成後,打壓力測試時,發現接入層機器端口占滿而致使服務異常,開始追查問題。html

追查方向

深刻了解grpc協議

  • gRPC是一個高性能、通用的開源 RPC 框架,其由 Google 主要面向移動應用開發並基於HTTP/2協議標準而設計,基於ProtoBuf(Protocol Buffers) 序列化協議開發,且支持衆多開發語言。gRPC 提供了一種簡單的方法來精確地定義服務和爲 iOS、Android 和後臺支持服務自動生成可靠性很強的客戶端功能庫。客戶端充分利用高級流和連接功能,從而有助於節省帶寬、下降的 TCP 連接次數、節省 CPU 使用、和電池壽命。

從上述描述能夠看出grpc基於http2,client到server應該保持長鏈接,理論上不該該出現端口占滿的問題linux

抓包

  • client到接入層抓包看請求狀態,發現client到接入層確實是長鏈接狀態。接入層到後端server抓包發現,請求並無保持長鏈接,一個請求處理完以後,連接就斷開了。

查nginx跟長鏈接相關配置

  • nginx長鏈接相關說明參考如下文檔:nginx長鏈接
  • nginx跟grpc長鏈接相關配置,發如今1.15.6版本引入了"grpc_socket_keepalive"跟grpc直接相關長鏈接配置

    Configures the 「TCP keepalive」 behavior for outgoing connections to a gRPC server. By default, the operating system’s settings are in effect for the socket. If the directive is set to the value 「on」, the SO_KEEPALIVE socket option is turned on for the socket.nginx

參考nginx長鏈接相關文檔作了配置調整以後,發現nginx到server依然是短鏈接git

將nginx升級到nginx1.15.6(升級過程當中因爲線上的nginx用到了lua模塊,碰到了lua模塊不適配問題,解決方案見連接lua模塊適配問題)配置grpc_socket_keepalive on,抓包發現,會有少許處理多個請求的長鏈接存在,但大部分依然是短鏈接。github

  • 開啓nginx debug模式

從debug日誌來看nginx確實嘗試重用連接,可是從實際抓包看,nginx的連接重用的狀況很是少,大部分都是請求處理完以後連接斷開,懷疑nginx對grpc反向代理支持的不夠理想。後端

調整端口回收策略

  • 回到問題自己,要解決的問題是接入層端口占滿,各類調整nginx的長鏈接配置並不能解決這個問題,就嘗試從tcp連接端口回收方面解決,大部分的tcp連接都處於TIME_WAIT狀態。框架

  • TIME_WAIT狀態的時間是2倍的MSL(linux裏一個MSL爲30s,是不可配置的),在TIME_WAIT狀態TCP鏈接實際上已經斷掉,可是該端口又不能被新的鏈接實例使用。這種狀況通常都是程序中創建了大量的短鏈接,而操做系統中對使用端口數量作了限制最大能支持65535個,TIME_WAIT過多很是容易出現鏈接數佔滿的異常。對TIME_WATI的優化有兩個系統參數可配置:tcp_tw_reuse,tcp_tw_recycle 這兩個參數在網上都有詳細介紹,這是就不展開來說socket

  • tcp_tw_reuse參考連接tcp

  • tcp_tw_recycle(Enable fast recycling TIME-WAIT sockets)參考連接post

  • 測試來看,開啓tcp_tw_reuse並無解決端口被佔滿的問題,因此開啓了更激進的tcp_tw_recycle,至此端口占用顯著下降,問題解決,因爲咱們接入層並非用的NAT,因此這個配置不會影響服務。

結論

  • 經過測試發現nginx對grpc的反向代理支持的不夠理想,從nginx到後端server大部分請求不能保持長鏈接
  • 當用nginx作接入層反向代理時,對tcp參數調優能夠避免端口占滿等問題的發生
相關文章
相關標籤/搜索