SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

2020年9月28日21:34:31 發(fā)表評(píng)論 4,619 ℃

阿湯博客前兩篇文章《SpringCloud Zuul(Ribbon)重試配置不生效解決辦法》和《SpringCloud Feign重試不生效問(wèn)題排查》已經(jīng)介紹了Ribbon和Feign重試不生效的原因,且已經(jīng)給出了解決辦法。

但是在經(jīng)過(guò)了兩天的實(shí)際測(cè)試后發(fā)現(xiàn),不同的超時(shí)時(shí)間配置、重試機(jī)制和熔斷時(shí)間,都會(huì)影響重試的實(shí)際效果。這里分幾個(gè)場(chǎng)景:

場(chǎng)景一:請(qǐng)求直接通過(guò)Zuul調(diào)用服務(wù)A,而此服務(wù)A的接口沒(méi)有其他服務(wù)的Feign調(diào)用,這種場(chǎng)景比較簡(jiǎn)單,主要受Zuul的ribbon配置影響。

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

Zuul的重試參數(shù)配置:

ribbon:
  ConnectTimeout: 1000
  ReadTimeout: 1000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 1
  OkToRetryOnAllOperations: false
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 4000 
zuul:
  retryable: true

這里A服務(wù)節(jié)點(diǎn)健康狀態(tài)分為兩種情況

情況一:

當(dāng)A服務(wù)節(jié)點(diǎn)都出現(xiàn)故障,此時(shí)請(qǐng)求首先通過(guò)Zuul負(fù)載均衡訪問(wèn)任意A服務(wù)節(jié)點(diǎn)比如A1,A1節(jié)點(diǎn)訪問(wèn)超時(shí),然后觸發(fā)MaxAutoRetriesNextServer=1的重試請(qǐng)求A2,然后A2節(jié)點(diǎn)返回超時(shí),最后瀏覽器響收到非200狀態(tài)的返回,請(qǐng)求總用時(shí)ribbon.ReadTimeout * (1+MaxAutoRetriesNextServer)=2s左右,如下圖:

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

情況二:

當(dāng)A2節(jié)點(diǎn)出現(xiàn)故障的時(shí)候,此時(shí)請(qǐng)求首先通過(guò)Zuul負(fù)載均衡訪問(wèn)任意A服務(wù)節(jié)點(diǎn),如果剛好此時(shí)負(fù)載到A2,那么請(qǐng)求超時(shí)觸發(fā)MaxAutoRetriesNextServer=1的重試請(qǐng)求A1,然后A1返回正常結(jié)果,最后瀏覽器響收到200狀態(tài)的正常結(jié)果,請(qǐng)求總用時(shí)為ribbon.ReadTimeout + A1處理請(qǐng)求的時(shí)間,大約1s左右。實(shí)際上當(dāng)A2響應(yīng)的失敗請(qǐng)求到達(dá)一定的數(shù)量或者百分比之后會(huì)觸發(fā)熔斷,那么在熔斷時(shí)間內(nèi),請(qǐng)求不會(huì)轉(zhuǎn)發(fā)到A2,熔斷的默認(rèn)配置如下:

#當(dāng)在配置時(shí)間窗口內(nèi)達(dá)到此數(shù)量的失敗后,進(jìn)行短路。默認(rèn)20個(gè)

hystrix.command.default.circuitBreaker.requestVolumeThreshold=20

#短路多久以后開(kāi)始嘗試是否恢復(fù),默認(rèn)5s

hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5

#出錯(cuò)百分比閾值,當(dāng)達(dá)到此閾值后,開(kāi)始短路。默認(rèn)50%

hystrix.command.default.circuitBreaker.errorThresholdPercentage=50%

當(dāng)把參數(shù)MaxAutoRetries更改為1時(shí):

上面的情況一定會(huì)有4次超時(shí)請(qǐng)求,即A1節(jié)點(diǎn)超時(shí),首先觸發(fā)MaxAutoRetries=1本節(jié)點(diǎn)A1重試和MaxAutoRetriesNextServer=1的A2節(jié)點(diǎn)重試,然后A2節(jié)點(diǎn)返回超時(shí),再次觸發(fā)MaxAutoRetries=1本節(jié)點(diǎn)A2的重試,所以A1和A2都有2次處理請(qǐng)求,最后瀏覽器返回的請(qǐng)求總時(shí)間為:ribbon.ReadTimeout * (1+MaxAutoRetriesNextServer)*(1+MaxAutoRetries)=4s左右。

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

情況二最多會(huì)有3次請(qǐng)求,第一次請(qǐng)求故障節(jié)點(diǎn)A2,會(huì)有本節(jié)點(diǎn)A2的一次重試并超時(shí),和A1節(jié)點(diǎn)的重試,并訪問(wèn)正常結(jié)果,此時(shí)請(qǐng)求總用時(shí)為ribbon.ReadTimeout *(MaxAutoRetries +MaxAutoRetriesNextServer)+ A1處理請(qǐng)求的時(shí)間=2s左右。

場(chǎng)景二:這種場(chǎng)景實(shí)際使用過(guò)程中并不多見(jiàn),這里主要為了測(cè)試Feign默認(rèn)配置。

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

服務(wù)A已引入Spring Retry組件配置如下:

${spring.application.name}:  
  ribbon:
    MaxAutoRetries: 1
    MaxAutoRetriesNextServer: 1 
    OkToRetryOnAllOperations: false
    NFLoadBalancerRuleClassName: AvailabilityFilteringRule
feign:
  client:
    config:
      default:
        connectTimeout: 1000
        readTimeout: 1000

這里簡(jiǎn)單說(shuō)下測(cè)試結(jié)果,不做詳細(xì)分析:

1、服務(wù)B節(jié)點(diǎn)都故障:

沒(méi)有配置ribbon的情況下,B1節(jié)點(diǎn)處理1次,重試1次,兩次失敗以后B1對(duì)B2重試1次,B2對(duì)B2重試一1次,或者訪問(wèn)(B1/B2)1次,然后重試3次,然而和之前查的資料feign默認(rèn)重試5次并不相同,最后總的超時(shí)請(qǐng)求時(shí)間為 feign.readTimeout * 4。

按照上面的配置以后,請(qǐng)求訪問(wèn)任意節(jié)點(diǎn)一次,然后對(duì)另外一個(gè)節(jié)點(diǎn)進(jìn)行重試一次,總的超時(shí)請(qǐng)求時(shí)間為feign.readTimeout * 2。

另外實(shí)際測(cè)試結(jié)果不管怎么更改服務(wù)A配置里面的ribbon.MaxAutoRetries和ribbon.MaxAutoRetriesNextServer次數(shù),重試結(jié)果都不變,后續(xù)再對(duì)源碼進(jìn)行分析。

2、服務(wù)B有一個(gè)節(jié)點(diǎn)故障的情況這里也不多做介紹,實(shí)際場(chǎng)景并不多見(jiàn)。

場(chǎng)景三:生產(chǎn)過(guò)程中大部分場(chǎng)景都類似此場(chǎng)景,即多服務(wù)之間的多跨度調(diào)用。

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

由于此場(chǎng)景比較復(fù)雜,而且影響重試效果的配置比較多,這里以網(wǎng)關(guān)的MaxAutoRetries和MaxAutoRetriesNextServer配置不變,并且B1節(jié)點(diǎn)故障,B2節(jié)點(diǎn)正常;分為A服務(wù)是否引入spring-retry組件,Zuul網(wǎng)關(guān)的ribbon.ReadTimeout等于和大于服務(wù)端feign.client.config.default.readTimeout4種情況測(cè)試分析。

Zuul重試參數(shù)配置如下(hystrix的時(shí)間,根據(jù)readtimeout的值動(dòng)態(tài)變化,后面不另做說(shuō)明,公式為(ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)):

ribbon:
  ConnectTimeout: 1000
  ReadTimeout: 1000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 1
  OkToRetryOnAllOperations: false
  NFLoadBalancerRuleClassName: AvailabilityFilteringRule
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 4000 
zuul:
  retryable: true

服務(wù)A的配置如下:

${spring.application.name}:  
  ribbon:
    MaxAutoRetries: 1
    MaxAutoRetriesNextServer: 1 
    OkToRetryOnAllOperations: false
    NFLoadBalancerRuleClassName: AvailabilityFilteringRule
feign:
  client:
    config:
      default:
        connectTimeout: 1000
        readTimeout: 1000

情況一:服務(wù)A未引入spring-retry組件,Zuul網(wǎng)關(guān)的ribbon.ReadTimeout等于服務(wù)端feign.client.config.default.readTimeout 都為1s。

1、此種情況,小概率會(huì)出現(xiàn)A1訪問(wèn)B1超時(shí),觸發(fā)Zuul ribbon重試A2,A2又通過(guò)Feign訪問(wèn)到B1的情況(因?yàn)榍懊娼榻B過(guò),B1失敗請(qǐng)求到達(dá)一定的數(shù)量或者百分比之后會(huì)觸發(fā)熔斷),通過(guò)JMeter并發(fā)測(cè)試5組,大概會(huì)有一組中的1個(gè)請(qǐng)求會(huì)出現(xiàn)超時(shí)的情況如下圖,其他時(shí)候第一次訪問(wèn)B1超時(shí),重試的時(shí)候A服務(wù)不會(huì)再負(fù)載到B1服務(wù)。

2、單次訪問(wèn)測(cè)試和JMeter并發(fā)測(cè)試,得到的結(jié)果一致。

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

情況二:服務(wù)A未引入spring-retry組件,Zuul網(wǎng)關(guān)的ribbon.ReadTimeout=3000 大于服務(wù)端 feign.client.config.default.readTimeout =1000的值。

1、單次訪問(wèn)的時(shí)候,當(dāng)A服務(wù)feign負(fù)載到B1,瀏覽器馬上就返回了500錯(cuò)誤,總耗時(shí)1s,而且通過(guò)B1和B2服務(wù)的日志觀察并沒(méi)有進(jìn)行重試。

2、通過(guò)JMeter并發(fā)測(cè)試5組,即使有些請(qǐng)求超過(guò)1s(表示負(fù)載到了B1),但是實(shí)際返回的狀態(tài)碼是200,說(shuō)明已經(jīng)進(jìn)行了重試,并且5組1萬(wàn)次請(qǐng)求,未出現(xiàn)非200狀態(tài)的請(qǐng)求,如下圖:

3、得出結(jié)論高并發(fā)的時(shí)候重試邏輯并非和低頻訪問(wèn)得到的結(jié)果一致,這個(gè)只有等空閑的時(shí)候研究源碼才知道其中的處理邏輯。

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

情況三:服務(wù)A引入spring-retry組件,Zuul網(wǎng)關(guān)的ribbon.ReadTimeout=3000 大于服務(wù)端 feign.client.config.default.readTimeout =1000的值。

1、單次訪問(wèn)的時(shí)候,當(dāng)A1服務(wù)feign負(fù)載到B1返回超時(shí),馬上A1就會(huì)重試訪問(wèn)B2,隨后瀏覽器馬上就返回了請(qǐng)求成功,總耗時(shí)1s,觀察B1和B2服務(wù)的日志,的確進(jìn)行了重試,觀察A1和A2服務(wù)日志,調(diào)用B1的時(shí)候并沒(méi)有返回接口超時(shí)的日志,也說(shuō)明了A1進(jìn)行的重試調(diào)用了B1,所以不管怎么樣,瀏覽器都會(huì)返回正常結(jié)果。

2、通過(guò)JMeter并發(fā)測(cè)試5組,實(shí)際得到結(jié)果,也是和單次訪問(wèn)一樣,100%請(qǐng)求都會(huì)返回正常結(jié)果,但是單次請(qǐng)求最大時(shí)間卻達(dá)到了2s,加上1s的請(qǐng)求,基本上占了整個(gè)請(qǐng)求數(shù)量的50%以上。

3、通過(guò)JMeter的聚合結(jié)果和之前情況一和情況二對(duì)比,性能下降了7倍左右,QPS只有23-28左右如下圖,而前面的實(shí)測(cè)QPS基本保持在150以上。

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

情況四:服務(wù)A引入spring-retry組件,Zuul網(wǎng)關(guān)的ribbon.ReadTimeout等于服務(wù)端feign.client.config.default.readTimeout 都為1s。

1、單次訪問(wèn)的時(shí)候,只有A服務(wù)第一次就負(fù)載到B2節(jié)點(diǎn)瀏覽器100%才會(huì)返回正常結(jié)果,A服務(wù)第一次訪問(wèn)到B1超時(shí),剛好A服務(wù)重試訪問(wèn)B2返回了結(jié)果,此時(shí)剛好zuul還沒(méi)觸發(fā)A服務(wù)的重試,瀏覽器才會(huì)返回正常結(jié)果,其他情況瀏覽器都會(huì)返回500錯(cuò)誤,而且B服務(wù)大概率都會(huì)產(chǎn)生2、3或者4次請(qǐng)求。

2、通過(guò)JMeter并發(fā)測(cè)試5組,實(shí)際得到結(jié)果,也是和單次訪問(wèn)一樣,返回成功的請(qǐng)求占很少一部分,基本在5%左右,如下圖:

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

SpringCloud Ribbon和Feign重試參數(shù)性能實(shí)測(cè)對(duì)比

通過(guò)上面四種情況的測(cè)試,得出結(jié)論:

1、高并發(fā)的時(shí)候開(kāi)啟feign的重試,當(dāng)有一個(gè)服務(wù)某個(gè)節(jié)點(diǎn)故障時(shí),會(huì)嚴(yán)重影響系統(tǒng)性能。

2、服務(wù)端開(kāi)啟feign重試機(jī)制后,如果網(wǎng)關(guān)ribbon超時(shí)時(shí)間和服務(wù)feign超時(shí)時(shí)間設(shè)置不當(dāng),當(dāng)某個(gè)服務(wù)某一個(gè)節(jié)點(diǎn)出現(xiàn)故障時(shí),會(huì)嚴(yán)重影響請(qǐng)求返回的成功率。

3、建議不要開(kāi)啟服務(wù)端的feign的重試機(jī)制,只要重試就會(huì)產(chǎn)生多余的請(qǐng)求,影響系統(tǒng)性能,所以建議把ribbon.MaxAutoRetries設(shè)置為0。

4、當(dāng)服務(wù)端未開(kāi)啟feign重試時(shí),建議ribbon.ReadTimeout和feign.client.config.default.readTimeout設(shè)置為一樣,這樣即使某個(gè)服務(wù)某個(gè)節(jié)點(diǎn)故障,即便是高并發(fā)或者低頻訪問(wèn),都不會(huì)對(duì)訪問(wèn)返回成功率和性能造成太大的影響。

5、而實(shí)際生產(chǎn)過(guò)程中,一般A服務(wù)的接口都不止調(diào)用一個(gè)B服務(wù),甚至可能同時(shí)調(diào)用C、D、E服務(wù);B服務(wù)可能還會(huì)調(diào)用F服務(wù)等等非常復(fù)雜,所以情況二,實(shí)際過(guò)程中也可以把ribbon.ReadTimeout設(shè)置稍微大于feign.client.config.default.readTimeout,模擬某個(gè)服務(wù)某個(gè)節(jié)點(diǎn)故障,進(jìn)行并發(fā)性能測(cè)試對(duì)比。

【騰訊云】云服務(wù)器、云數(shù)據(jù)庫(kù)、COS、CDN、短信等云產(chǎn)品特惠熱賣中

發(fā)表評(píng)論

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: