1. 程式人生 > 其它 >Hystrix-資源隔離策略(執行緒、訊號量)

Hystrix-資源隔離策略(執行緒、訊號量)

1.為什麼要進行資源隔離

比如我們現在有3個業務呼叫分別是查詢訂單、查詢商品、查詢使用者,且這三個業務請求都是依賴第三方服務-訂單服務、商品服務、使用者服務。三個服務均是通過RPC呼叫。當依賴的訂單服務變慢了,而這個時候後續有大量的查詢訂單請求過來,那麼容器中的執行緒數量則會持續增加直致CPU資源耗盡到100%,整個服務對外不可用,叢集環境下就是雪崩。所以,有必要將多個依賴服務的呼叫分別隔離到各自自己的資源池內,不對其他服務造成影響。如下圖:

2. 兩種隔離方式

2.1 執行緒隔離

適用場景:適合絕大多數的場景,對依賴服務的網路呼叫timeout,TPS要求高的這種問題
執行依賴程式碼的執行緒與請求執行緒(比如Tomcat執行緒)分離,請求執行緒可以自由控制離開的時間,這也是我們通常說的非同步程式設計,Hystrix是結合RxJava來實現的非同步程式設計。通過為每個包裹了HystrixCommand的API介面設定獨立的、固定大小的執行緒池(hystrix.threadpool.default.coreSize)來控制併發訪問量,當執行緒飽和的時候可以拒絕服務(走fallback方法),防止依賴問題擴散。
線上建議執行緒池不要設定過大,否則大量堵塞執行緒有可能會拖慢伺服器。

2.1.1 執行緒池隔離的優缺點

優點:
一個依賴呼叫可以給予一個執行緒池,這個依賴的異常不會影響其他的依賴。
使用執行緒可以完全隔離業務程式碼,請求執行緒可以快速返回。
可以完全模擬非同步呼叫,方便非同步程式設計。

缺點:
使用執行緒池的缺點主要是增加了計算的開銷。每一個依賴呼叫都會涉及到佇列,排程,上下文切換,而這些操作都有可能在不同的執行緒中執行。

2.1.2 執行緒池隔離相關引數

  讓我們來逐個介紹下@HystrixCommand註解的各個引數:
  1:commandKey:配置全域性唯一標識服務的名稱,比如,庫存系統有一個獲取庫存服務,那麼就可以為這個服務起一個名字來唯一識別該服務,如果不配置,則預設是@HystrixCommand註解修飾的函式的函式名。


  2:groupKey:一個比較重要的註解,配置全域性唯一標識服務分組的名稱,比如,庫存系統就是一個服務分組。通過設定分組,Hystrix會根據組來組織和統計命令的告、儀表盤等資訊。Hystrix命令預設的執行緒劃分也是根據命令組來實現。預設情況下,Hystrix會讓相同組名的命令使用同一個執行緒池,所以我們需要在建立Hystrix命令時為其指定命令組來實現預設的執行緒池劃分。此外,Hystrix還提供了通過設定threadPoolKey來對執行緒池進行設定。建議最好設定該引數,使用threadPoolKey來控制執行緒池組。

  例如有如下程式碼:

dashboard為

說明:

findById - HystrixCommandKey(預設為Controller下的方法名)
MovieController 
- HystrixThreadPoolKey(不配置的情況下就是commandGroupKey,HystrixCommandGroupKey預設為類名)

  3:threadPoolKey:對執行緒池進行設定,細粒度的配置,相當於對單個服務的執行緒池資訊進行設定,也可多個服務設定同一個threadPoolKey構成執行緒組。

  4:fallbackMethod:@HystrixCommand註解修飾的函式的回撥函式,@HystrixCommand修飾的函式必須和這個回撥函式定義在同一個類中,因為定義在了同一個類中,所以fackback method可以是public/private均可。

  5:commandProperties:配置該命令的一些引數,如executionIsolationStrategy配置執行隔離策略,預設是使用執行緒隔離,此處我們配置為THREAD,即執行緒池隔離。參見:com.netflix.hystrix.HystrixCommandProperties中各個引數的定義。

  6:threadPoolProperties:執行緒池相關引數設定,具體可以設定哪些引數請見:com.netflix.hystrix.HystrixThreadPoolProperties

  7:ignoreExceptions:呼叫服務時,除了HystrixBadRequestException之外,其他@HystrixCommand修飾的函式丟擲的異常均會被Hystrix認為命令執行失敗而觸發服務降級的處理邏輯(呼叫fallbackMethod指定的回撥函式),所以當需要在命令執行中丟擲不觸發降級的異常時來使用它,通過這個引數指定,哪些異常丟擲時不觸發降級(不去呼叫fallbackMethod),而是將異常向上丟擲。

  8:observableExecutionMode:定義hystrix observable command的模式;

  9:raiseHystrixExceptions:任何不可忽略的異常都包含在HystrixRuntimeException中;

  10:defaultFallback:預設的回撥函式,該函式的函式體不能有入參,返回值型別與@HystrixCommand修飾的函式體的返回值一致。如果指定了fallbackMethod,則fallbackMethod優先順序更高。

2.2 訊號量隔離

用於隔離原生代碼或可快速返回的遠端呼叫(如memcached,redis)可以直接使用訊號量隔離,降低執行緒隔離的上下文切換開銷。

執行緒隔離會帶來執行緒開銷,有些場景(比如無網路請求場景)可能會因為用開銷換隔離得不償失,為此hystrix提供了訊號量隔離。

主要適用場景: 併發需求不大的依賴呼叫(因為如果併發需求較大,相應的訊號量的數量就要設定得夠大,因為Tomcat執行緒與處理執行緒為同一個執行緒,那麼這個依賴呼叫就會佔用過多的Tomcat執行緒資源,有可能會影響到其他服務的接收)

和執行緒池隔離類似,同一個HystrixCommandGroupKey共用一個訊號量(預設為類名)

public class CommandUsingSemaphoreIsolation extends HystrixCommand<String> {
    private final int id;
    public CommandUsingSemaphoreIsolation(int id) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                // since we're doing an in-memory cache lookup we choose SEMAPHORE isolation
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));
        this.id = id;
    }
    @Override
    protected String run() {
        // a real implementation would retrieve data from in memory data structure
        return "ValueFromHashMap_" + id;
   }
}

2.3 執行緒池隔離與訊號量隔離區別

官方圖示:

不積跬步,無以至千里;不積小流,無以成江海