伺服器容錯保護二

zhumeilu發表於2017-12-14

###觸發斷路器的流程

客戶端通過RestTemplate呼叫遠端服務,如果在RestTemplate內部已經實現了重試的話,則還會進行重試,當重試完成後,結果還是失敗,則會呼叫fallback裡面指定的方法。

1 建立HyStrixCommand或者HystrixObservableCommand物件 2 執行命令 3 結果是否被快取 如果開啟了快取,並且該命令快取命中,則快取的結果立即以Observable物件的形式返回 4 斷路器是否開啟

  • 如果斷路器是開啟的,那麼Hystrix不會執行命令,而是轉接到fallback處理邏輯
  • 如果斷路器關閉,Hystrix跳到第5步 5 執行緒池/請求佇列/訊號量是否佔滿(依賴隔離,每個依賴服務都有獨立的執行緒池)
  • 如果佔滿,則直接轉到fallback處理邏輯 6 執行run,construct,使用者自己寫的RestTemplate請求服務 7 計算斷路器的健康度 Hystrix會將“成功”、“失敗”、“拒絕”、“超時”等資訊報告給斷路器,而斷路器會維護一組計數器來統計這些資料。斷路器會使用這些統計資料來決定是否將斷路器開啟,來對某個依賴服務的請求進行“熔斷/短路”,直到恢復期結束。若在恢復期結束後,根據統計的資料判斷如果還是未達到健康指標,就再次“熔斷/短路” 8 fallback處理 當命令執行失敗時,則會進入fallback,通常稱之為“服務降級” 原因: 4,當前命令處於“熔斷/短路”狀態,斷路器是開啟的時候 5,當前命令的執行緒池、請求佇列或者訊號量被佔滿 6,HystrixObservableCommand.construct()或者HystrixCommand.run()丟擲異常 9 返回成功的響應

###使用詳解(註解版用法) 基本使用: 建立同步執行

    @HystrixCommand
    public String hello(){

       return restTemplate.getForObject("http://eureka-client/hello",String.class);
    }
複製程式碼

####建立非同步執行

    @HystrixCommand
    public Future<String> helloAsync(){

        return new AsyncResult<String>() {
            @Override
            public String invoke() {
                return restTemplate.getForObject("http://eureka-client/hello",String.class);
            }
        };
    }
複製程式碼

####定義服務降級

    @HystrixCommand(fallbackMethod = "helloFallback")
    public String hello(){

       return restTemplate.getForObject("http://eureka-client/hello",String.class);
    }

    public String helloFallback(){
        return "hello";
    }
複製程式碼

當降級的服務邏輯返回的結果也有可能不穩定時,也可以對其進行降級

    @HystrixCommand(fallbackMethod = "helloFallback")
    public String hello(){

       return restTemplate.getForObject("http://eureka-client/hello",String.class);
    }
    @HystrixCommand(fallbackMethod = "helloDefaultFallback")
    public String helloFallback(){
        return restTemplate.getForObject("http://eureka-client/hello2",String.class);
    }

    public String helloDefaultFallback(){
        return "hello";
    }

複製程式碼

有些情況也可以不需要實現降級邏輯 比如:執行寫操作邏輯的命令,執行批處理或離線計算的命令,當這些操作出現異常時,只需要返回錯誤結果即可,不需要進行降級處理。

####異常傳播 在HystrixCommand實現的run()方法中丟擲異常時,除了HystrixBadRequestException之外,其他的異常均會被Hystrix認為命令執行失敗,並觸發服務降級的處理邏輯。@HystrixCommand註解的ignoreException引數可以忽略指定異常

    @HystrixCommand(ignoreExceptions = {BadRequestException.class})
    public String hello(){

        return restTemplate.getForObject("http://eureka-client/hello",String.class);
    }
複製程式碼

當hello方法丟擲了型別為BadRequestException的異常時,Hystrix會將它包裝在HystrixBadRequestException中丟擲,這樣就不會觸發後續的fallback邏輯。 ####異常獲取

    @HystrixCommand(fallbackMethod = "helloFallback")
    public String hello(){

        return restTemplate.getForObject("http://eureka-client/hello",String.class);
    }
    public String helloFallback(Throwable e){
        return "hello";
    }
複製程式碼

通過在降級方法引數列表中加入Throwable 型別的引數,獲取異常物件e,可以通過獲取異常資訊或者異常型別來進行處理。

####命令名稱、分組以及執行緒池劃分 通過設定命令組,Hystrix會根據組來組織和統計命令的告警、儀表盤等資訊。那麼為什麼一定要設定命令組呢?因為除了根據組能實現統計之外,Hystrix命令預設的執行緒劃分也是根據命令分組來實現的。預設情況下,Hystrix會讓相同組名的命令使用同一個執行緒池,所以我們需要在建立Hystrix命令時為其指定命令組名來實現預設的執行緒池劃分。 Hystrix還提供了HystrixThreadPoolKey來對執行緒池進行設定,通過它我們可以實現更細粒度的執行緒池的劃分。 如果在沒有特別指定HystrixThreadPoolKey的情況下,依然會使用命令組的方式來劃分執行緒池。通常情況下,儘量通過HystrixThreadPoolKey的方式來指定執行緒池的劃分,而不是通過組名的預設方式實現劃分,因為多個不同的命令可能從業務邏輯上來看屬於同一個組,但是往往從實現本身上需要跟其他命令進行隔離。

    @HystrixCommand(commandKey = "hello",groupKey = "testGroup",threadPoolKey = "helloThread")
    public String hello(){

        return restTemplate.getForObject("http://eureka-client/hello",String.class);
    }
複製程式碼

####請求快取

  • 設定請求快取
    @CacheResult
    @HystrixCommand
    public User getUserById(Long id){
        return restTemplate.getForObject("http://eureka-client/getUserById?id={1}",User.class,id);
    }
複製程式碼

通過新增 @CacheResult註解,開啟快取功能。快取的key值使用所有的引數生成。

  • 定義快取key 第一種方式
    @CacheResult(cacheKeyMethod = "getUserByIdCacheKey")
    @HystrixCommand
    public User getUserById(Long id){
        return restTemplate.getForObject("http://eureka-client/getUserById?id={1}",User.class,id);
    }

    private Long getUserByIdCacheKey(Long id){
        return id;
    }
複製程式碼

第二種方式

    @CacheResult
    @HystrixCommand
    public User getUserById(@CacheKey("id") Long id){
        return restTemplate.getForObject("http://eureka-client/getUserById?id={1}",User.class,id);
    }
複製程式碼

第二種方式@CacheKey的優先順序比第一種方式cacheKeyMethod 的優先順序低 第二種方式還支援訪問User物件裡面的id屬性

  • 快取清理 當使用了update操作更新了物件時,那麼就需要對快取進行清理
    @CacheResult
    @HystrixCommand
    public User getUserById(@CacheKey("id") Long id){
        return restTemplate.getForObject("http://eureka-client/getUserById?id={1}",User.class,id);
    }
    @CacheRemove(commandKey = "getUserById")
    @HystrixCommand
    public User update(User user){
        return restTemplate.postForObject("http://eureka-client/update",User.class,user);
    }
複製程式碼

####請求合併 微服務架構中的依賴通常通過遠端呼叫實現,而遠端呼叫中最常見的問題就是通訊消耗與連線數佔用。在高併發的情況下,因通訊次數的增加,總的通訊時間消耗將會變得不那麼理想。因為依賴服務端 執行緒池資源有限,將會出現排隊等待與響應延遲的情況。Hystrix提供了HystrixCollapser來實現請求的合併,以減少通訊消耗和執行緒數的佔用。 HystrixCollapser實現了在HystrixCommand之前放置一個合併處理器,將處於一個很短的時間窗(預設10毫秒)內對同一依賴服務的多個請求進行整合並以批量方式發起請求的功能(服務提供方也需要提供相應的批量實現介面)。

    @HystrixCollapser(batchMethod = "batchGetUserByIds",collapserProperties = {
            @HystrixProperty(name="timerDelayInMilliseconds",value="100")
    })
    public User getUserById(Long id){
        return restTemplate.getForObject("http://eureka-client/getUserById?id={1}",User.class,id);
    }
    public List<User> batchGetUserByIds(List<Long> ids){
        return restTemplate.getForObject("http://eureka-client/batchGetUserByIds?ids={1}",List.class, StringUtils.join(ids,","));
    }
複製程式碼

請求合併額外開銷

####屬性詳解

相關文章