spring.cloud.gateway.httpclient.connect-timeout=2000 # 全域性的TCP連線超時時間預設時間是45秒,所以也就是發生網路故障的時候,連線時間要等待45秒,而網路連線是同步阻塞的 ,The connect timeout in millis, the default is 45s. 所以就會導致請求非常慢,從閘道器就卡死了。 spring.cloud.gateway.httpclient.response-timeout=PT30S #全域性的響應超時時間,網路連結後,後端服務多久不返回閘道器就報錯 The response timeout.
System.setProperty("reactor.netty.pool.leasingStrategy", "lifo");
spring.cloud.gateway.httpclient.pool.max-idle-time=PT1S
spring.cloud.gateway.httpclient.connect-timeout=2000
spring.cloud.gateway.httpclient.response-timeout=PT30S
是不是略感陌生,平時的閘道器使用的都是預設配置,使用起來也沒有問題,因為使用了WebFlux非同步非阻塞的框架,底層基於Netty,NIO的非同步非阻塞的處理SOCKET,可以讓少量的執行緒處理大量的業務請求。
所以如果你使用端點監控去檢視閘道器的執行緒數,閘道器中暴露的執行緒數都不會很高
system_cpu_count{application="i5xforyou-service-gateway",} 4.0
jvm_threads_live_threads{application="i5xforyou-service-gateway",} 123.0
jvm_threads_states_threads{application="i5xforyou-service-gateway",state="new",} 0.0
jvm_threads_states_threads{application="i5xforyou-service-gateway",state="runnable",} 21.0
jvm_threads_states_threads{application="i5xforyou-service-gateway",state="blocked",} 0.0
jvm_threads_states_threads{application="i5xforyou-service-gateway",state="waiting",} 70.0
jvm_threads_states_threads{application="i5xforyou-service-gateway",state="timed-waiting",} 32.0
jvm_threads_states_threads{application="i5xforyou-service-gateway",state="terminated",} 0.0
事情的起源還是從一次網路故障說起,由於機房網路的LBC網關出現問題,導致整體系統的內網網路出現延遲,各服務內部呼叫,介面間呼叫以及連線資料庫等所有節點都發生了超時,延遲擁塞,導致外部反饋使用者在頁面上卡死,所有服務都出現了延遲請求,請求呼叫非常慢,致使整個系統出現了雪崩效應,故障持續了相當長的時間,而且找不到原因,只知道網路問題卻無從定位,也不知道何時恢復。從日誌看閘道器報各種錯,CPU的負載也相當高,呈現忽高忽低的抖動的狀態。
scg中各種報錯,主要是 Connect reset by peer 和 Connection prematurely closed BEFORE response,
說來也巧,因為事故前上線給部分服務及閘道器加了Alibaba的Sentinel的的限流降級元件,其中就各分配了50%的流量分別到2個隔離的服務叢集中,雖然當時2個叢集都發生了網路問題,因為同屬於一個網路分割槽內,但是加了sentinel的這部分叢集的閘道器及服務的資源並沒有配置足夠,有問題的這部分scg,當時配置了8臺,而沒有問題的scg配置了18臺,後端服務的資源也是沒有按1:1的比例配足,最碰巧的是後來重啟了後端服務及這8臺scg, 也沒有恢復問題,而關掉了這8臺網關請求居然恢復了。就是這種種的巧合以至於後來的百思不得其解及事故調查分析。甚至一度被懷疑是Sentinel把請求阻塞了,雖然事故原因是由於機房網路引起的,但是重啟閘道器服務恢復這一表象卻難以說服。最後可以解釋的原因就是故障最後網路問題已經緩解,同時進入到8臺scg的流量因為伴隨著scg關閉,而流量都進入到了18臺scg中。哪一部分叢集的資源比較充足。
和閘道器相關的調查及分析也就引出了這個4個重要的引數,首先要說明的是,這些引數的配置不會導致真正發生網路問題的時候能夠快速解決故障的問題,因為網路故障是非常致命及難易管控的,除非具備相應的容災處理方案,例如多機房雙活多活等,否則難易做到對業務的無損操作。所以這些引數的作用不能解決和杜絕網路問題,只能說是讓閘道器效能或者功能得到進一步最佳化的效果,從而快速定位問題並配合降級保護等策略使服務及scg閘道器可以得到一些保護。
首先我們調查了閘道器報錯,Connection prematurely closed BEFORE response,透過日誌監控發現,報錯的出現都是在重啟8臺scg的期間發生的
時間點比較吻合,透過查詢網上文章 https://blog.csdn.net/rickiyeat/article/details/107900585
裡面描述了報錯的問題及解決方法,雖然我們後端沒有使用tomcat,使用的undertow,但是原理應該是一樣的就是後端服務主動斷開了連線,因為當時懷疑是服務故障所以就重啟了後端服務,而這時scg中的連線依然使用了和後端保持的連線,而請求傳送的時候後端連線已經斷開而導致的報錯。
請求報錯reactor.netty.http.client.PrematureCloseException
就會丟擲Connection prematurely closed BEFORE response的異常
和這個相關的配置引數有2個:
spring.cloud.gateway.httpclient.pool.max-idle-time=PT1S # 這個引數的作用就是scg的空閒連線超時回收時間
System.setProperty("reactor.netty.pool.leasingStrategy", "lifo"); #這個引數的作用是先使用後回收的連線,而不是先使用先回收的連線。所以這2個引數的配合使用,可以讓閘道器始終能夠取到一個比較新鮮的連線。而不會導致後端連線中斷而Scg的連線取到的是一個是比較舊的並且很可能是一個後端已經主動斷開的連線。
還有2個重要的引數
spring.cloud.gateway.httpclient.connect-timeout=2000 # 全域性的TCP連線超時時間預設時間是45秒,所以也就是發生網路故障的時候,連線時間要等待45秒,而網路連線是同步阻塞的 ,The connect timeout in millis, the default is 45s. 所以就會導致請求非常慢,從閘道器就卡死了。
spring.cloud.gateway.httpclient.response-timeout=PT30S #全域性的響應超時時間,網路連結後,後端服務多久不返回閘道器就報錯 The response timeout.
當網路連線到達指定的時間,比如預設的45秒後,閘道器會報錯,日誌中會顯示一個 connection timed out的500異常
這也就是當天日誌顯示的大量的一個連線超時的報錯
實際就是由於網路連線發生了大量的超時,而因為預設超時時間是45秒,所以閘道器也一直在等待,理論上說,這個時間45秒還是比較長的,如果網路連線問題調成2秒超時,如果這期間大量的出現 connection reset by peer connection timed out,就可以斷定是網路發生了抖動或者故障。
請求響應時間也同樣,如果後端服務30秒內仍然未返回任何回覆資訊就會直接超時報錯,可以說正常響應超過1秒就已經難以忍受了。透過配置超時時間,如果超過超時時間,閘道器會直接報錯,
如果出現了,504 GATEWAY_TIMEOUT "Response took longer than timeout: ", Gateway Timeout 這樣的504錯誤,那麼就可以快速認定是由於後端服務問題導致的請求響應超時問題。
所以合理的配置對於快速定位分析問題能夠起到一定的促進作用。如果你還沒有配置,那麼就請留意這些引數的使用吧。