簡單談談什么是Hystrix,以及SpringCloud的各種超時時間配置效果,和簡單談談微服務優化


 

轉載於~~ https://blog.csdn.net/zzzgd_666/article/details/83314833 

1. 前言(以下的springcloud版本是Dalston.RC1)

以下的springcloud版本是Dalston.RC1
Springcloud框架中,超時時間的設置通常有三個層面:

  1. zuul網關
#默認1000
zuul.host.socket-timeout-millis=2000
#默認2000
zuul.host.connect-timeout-millis=4000
  • 1
  • 2
  • 3
  • 4
  1. ribbon

ribbon: OkToRetryOnAllOperations: false #對所有操作請求都進行重試,默認false ReadTimeout: 5000 #負載均衡超時時間,默認值5000 ConnectTimeout: 3000 #ribbon請求連接的超時時間,默認值2000 MaxAutoRetries: 0 #對當前實例的重試次數,默認0 MaxAutoRetriesNextServer: 1 #對切換實例的重試次數,默認1 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 熔斷器Hystrix
hystrix: command: default: #default全局有效,service id指定應用有效 execution: timeout: #如果enabled設置為false,則請求超時交給ribbon控制,為true,則超時作為熔斷根據 enabled: true isolation: thread: timeoutInMilliseconds: 1000 #斷路器超時時間,默認1000ms feign.hystrix.enabled: true 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.測試各個配置的效果

這里我開了一個Eureka服務中心
開了兩個個服務eureka-client,端口分別為80878088,進行負載均衡
開了一個服務eureka-feign去調用eureka-client的方法,模擬eureka-client處理時間過長的時候出現的情況

eureka-client的方法:

/** * 測試重試時間 * * @return */ @RequestMapping("/timeOut") public String timeOut(@RequestParam int mills) { log.info("[client服務-{}] [timeOut方法]收到請求,阻塞{}ms", port, mills); try { Thread.sleep(mills); } catch (InterruptedException e) { e.printStackTrace(); } log.info("[client服務-{}] [timeOut]返回請求",port); return String.format("client服務-%s 請求ok!!!", port); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

eureka-feign調用client的方法,通過傳參數mills來控制client線程休眠的時間

 /** * 測試重試時間 * @return */ @RequestMapping("/timeOut") public String timeOut(@RequestParam int mills){ log.info("開始調用"); return feignService.timeOut( mills ); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

service:

 /** * 測試springcloud的超時機制 * @param mills * @return */ @RequestMapping(value = "/timeOut",method = RequestMethod.GET) String timeOut(@RequestParam(value = "mills") int mills); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

熔斷方法:

 @Override public String timeOut(int mills) { System.out.println("熔斷"); return "熔斷了"; } 
  • 1
  • 2
  • 3
  • 4
  • 5

測試1

ribbon: OkToRetryOnAllOperations: false #對所有操作請求都進行重試,默認false ReadTimeout: 1000 #負載均衡超時時間,默認值5000 ConnectTimeout: 3000 #ribbon請求連接的超時時間,默認值2000 MaxAutoRetries: 0 #對當前實例的重試次數,默認0 MaxAutoRetriesNextServer: 1 #對切換實例的重試次數,默認1 hystrix: command: default: #default全局有效,service id指定應用有效 execution: timeout: #如果enabled設置為false,則請求超時交給ribbon控制,為true,則超時作為熔斷根據 enabled: true isolation: thread: timeoutInMilliseconds: 5000 #斷路器超時時間,默認1000ms 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 測試 900ms

在這里插入圖片描述
在這里插入圖片描述
請求正常.

  • 測試 2000ms
    熔斷
    在這里插入圖片描述

接着測試4000ms, 6000都熔斷了

測試2

更換兩個超時時間:

ReadTimeout: 3000   #負載均衡超時時間,默認值5000
ConnectTimeout: 1000 #ribbon請求連接的超時時間,默認值2000
  • 1
  • 2
ribbon: OkToRetryOnAllOperations: false #對所有操作請求都進行重試,默認false ReadTimeout: 3000 #負載均衡超時時間,默認值5000 ConnectTimeout: 1000 #ribbon請求連接的超時時間,默認值2000 MaxAutoRetries: 0 #對當前實例的重試次數,默認0 MaxAutoRetriesNextServer: 1 #對切換實例的重試次數,默認1 hystrix: command: default: #default全局有效,service id指定應用有效 execution: timeout: #如果enabled設置為false,則請求超時交給ribbon控制,為true,則超時作為熔斷根據 enabled: true isolation: thread: timeoutInMilliseconds: 5000 #斷路器超時時間,默認1000ms 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

測試2000ms:
成功了
在這里插入圖片描述

調用4000ms
熔斷了
在這里插入圖片描述

測試6000ms也是熔斷

可見ReadTimeoutConnectTimeout,當調用某個服務等待時間過長的時候, 對超時報錯/熔斷生效的是ReadTimeout,ConnectTimeout則表示連接服務的時間,一般不用配置太久,1~2秒左右就可以了

測試3

現在來測試ReadTimeouttimeoutInMilliseconds誰起作用
測試2中的配置如下:

 ReadTimeout: 3000   #負載均衡超時時間,默認值5000
  ConnectTimeout: 1000 #ribbon請求連接的超時時間,默認值2000
 timeoutInMilliseconds: 5000 #斷路器超時時間,默認1000ms
  • 1
  • 2
  • 3

在4000ms熔斷了,2000ms正常,說明是ReadTimeout生效, 現在換成:

ReadTimeout: 5000   #負載均衡超時時間,默認值5000
  ConnectTimeout: 1000 #ribbon請求連接的超時時間,默認值2000
 timeoutInMilliseconds: 3000 #斷路器超時時間,默認1000ms

  • 1
  • 2
  • 3
  • 4
ribbon: OkToRetryOnAllOperations: false #對所有操作請求都進行重試,默認false ReadTimeout: 5000 #負載均衡超時時間,默認值5000 ConnectTimeout: 1000 #ribbon請求連接的超時時間,默認值2000 MaxAutoRetries: 0 #對當前實例的重試次數,默認0 MaxAutoRetriesNextServer: 1 #對切換實例的重試次數,默認1 hystrix: command: default: #default全局有效,service id指定應用有效 execution: timeout: #是否開啟超時熔斷 enabled: true isolation: thread: timeoutInMilliseconds: 3000 #斷路器超時時間,默認1000ms feign.hystrix.enabled: true 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2000ms 正常
在這里插入圖片描述

4000ms 熔斷
在這里插入圖片描述

說明熔斷器timeoutInMilliseconds: 3000起作用了

測試4

這里再測一個配置:
這個enable如果為false, 則表示熔斷器不根據自己配置的超時時間進行熔斷,這樣的話就會收到ribbon的ReadTimeout配置的影響了,超過這個時間,eureka-feign會拋出timeout的異常,這個時候熔斷器就會因為這個異常而進行熔斷

hystrix:
  command:
    default:  #default全局有效,service id指定應用有效
      execution:
        timeout:
          #是否開啟超時熔斷
          enabled: false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

測試4000ms 正常
在這里插入圖片描述

測試6000ms 熔斷. 此處是因為ribbon的ReadTimeout: 5000
在這里插入圖片描述

3.總結

由上面的測試可以得出:

  1. 如果hystrix.command.default.execution.timeout.enabled為true,則會有兩個執行方法超時的配置,一個就是ribbon的ReadTimeout,一個就是熔斷器hystrix的timeoutInMilliseconds, 此時誰的值小誰生效
  2. 如果hystrix.command.default.execution.timeout.enabled為false,則熔斷器不進行超時熔斷,而是根據ribbon的ReadTimeout拋出的異常而熔斷,也就是取決於ribbon
  3. ribbon的ConnectTimeout,配置的是請求服務的超時時間,除非服務找不到,或者網絡原因,這個時間才會生效
  4. ribbon還有MaxAutoRetries對當前實例的重試次數,MaxAutoRetriesNextServer對切換實例的重試次數, 如果ribbon的ReadTimeout超時,或者ConnectTimeout連接超時,會進行重試操作
  5. 由於ribbon的重試機制,通常熔斷的超時時間需要配置的比ReadTimeout長,ReadTimeoutConnectTimeout長,否則還未重試,就熔斷了
  6. 為了確保重試機制的正常運作,理論上(以實際情況為准)建議hystrix的超時時間為:(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout
ribbon: OkToRetryOnAllOperations: false #對所有操作請求都進行重試,默認false ReadTimeout: 10000 #負載均衡超時時間,默認值5000 ConnectTimeout: 2000 #ribbon請求連接的超時時間,默認值2000 MaxAutoRetries: 0 #對當前實例的重試次數,默認0 MaxAutoRetriesNextServer: 1 #對切換實例的重試次數,默認1 hystrix: command: default: #default全局有效,service id指定應用有效 execution: timeout: enabled: true isolation: thread: timeoutInMilliseconds: 20000 #斷路器超時時間,默認1000ms 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4.微服務優化

4.1 什么是hystrix

我們先來看這么一個圖,假如訂單服務需要調用積分服務,庫存服務,倉儲服務,訂單服務的線程池有100個線程,這個時候積分服務突然掛了.這時候同時有大量的請求來訪問訂單服務,最終的結果是這100個線程都會卡在積分服務這里,這時候訂單服務也沒有多余的線程處理請求了,所以訂單服務也差不多掛了.
在這里插入圖片描述
這就是微服務中的服務雪崩問題.
而這時你會發現,如果我只是看看這個商品還有多少庫存,那么訂單服務就只需要調用庫存服務就可以了,並不受積分服務的影響.

這個時候就是Hystrix的時刻了,Hystrix是隔離、熔斷以及降級的一個框架。

Hystrix的特點,就是針對不同的服務,會搞很多個小小的線程池,比如訂單服務請求庫存服務是一個單獨的線程池,請求積分服務是一個單獨的線程池. 這樣雖然積分服務的線程池全部卡住了,但是不影響庫存服務的調用.

4.2 服務降級和熔斷

現在庫存服務的問題解決了,積分服務還沒解決啊. 比如積分服務網絡很差,訂單服務要一直傻等積分服務響應嗎?單單看一個請求,用戶等個幾秒可能還沒什么,如果100個線程都卡住幾秒,后面的請求全部得不到處理.
所以我們可以讓Hystrix在一定時間后主動返回,不再等待,這就是熔斷.

降級,顧名思義,就是將不重要或者不緊急的任務,延遲處理,或者暫不處理.比如上面的超時熔斷,熔斷了怎么辦?獲取不到用戶的積分,直接給用戶提示網絡繁忙,請稍后再試,就是一種延遲處理. 比如秒殺活動,為了防止並發量太大,通常會采取限流措施,降級后的處理方案可以是:排隊頁面(將用戶導流到排隊頁面等一會重試)、無貨(直接告知用戶沒貨了)、錯誤頁(如活動太火爆了,稍后重試)。

4.3 微服務優化

了解了Hystrix的特性和超時效果,再看看下面這個圖,服務A調用服務B和服務C,服務C沒有太復雜的邏輯處理,300毫秒內就處理返回了,服務B邏輯復雜,Sql語句就長達上百行,經常要卡個5,6秒返回,在大量請求調用到服務B的時候,服務A調用服務B的hystrix線程池已經不堪重負,全部卡住
在這里插入圖片描述
這里的話,首先考慮的就是服務B的優化,優化SQL,加索引,加緩存, 優化流程,同步改異步,總之縮短響應時間
一個接口,理論的最佳響應速度應該在200ms以內,或者慢點的接口就幾百毫秒。

a. 如何設置Hystrix線程池大小

Hystrix線程池大小默認為10

hystrix: threadpool: default: coreSize: 10 
  • 1
  • 2
  • 3
  • 4

每秒請求數 = 1/響應時長(單位s) * 線程數 = 線程數 / 響應時長(單位s)

也就是

線程數 = 每秒請求數 * 響應時長(單位s) + (緩沖線程數)

標准一點的公式就是QPS * 99% cost + redundancy count

比如一台服務, 平均每秒大概收到20個請求,每個請求平均響應時長估計在500ms,
線程數 = 20 * 500 / 1000 = 10
為了應對峰值高並發,加上緩沖線程,比如這里為了好計算設為5,就是 10 + 5 = 15個線程

b. 如何設置超時時間

還拿上面的例子,比如已經配置了總線程是12個,每秒大概20個請求,那么極限情況,每個線程都飽和工作,也就是每個線程一秒內處理的請求為 20 / 15 = ≈ 1.3個 , 那每個請求的最大能接受的時間就是 1000 / 1.3 ≈ 769ms ,往下取小值700ms.
實際情況中,超時時間一般設為比99.5%平均時間略高即可,然后再根據這個時間推算線程池大小


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM