服務降級——Hystrix

是什麼

Hystix是Netflix開源的一個延遲和容錯庫,用於隔離訪問遠程服務、第三方庫,防止出現級聯失敗。
這麼說,可能大家還是不明白,那Hystrix到底是什麼呢?

我們來模擬一個場景,在微服務的大環境下,一個請求可能調用多個後端服務,而某一個後端服務也可能需要調用其他的服務,如果此時,其中一個服務宕機或出現異常,導致服務無法返回結果,那麼調用這個服務的其他服務將進行等待狀態,如果此時又來了多個同樣的請求,那麼這些請求也將處於等待阻塞的狀態,由於服務器支持的線程和併發的數量有限,請求一直阻塞將導致服務器資源耗盡,從而導致所有其它服務都不可用,形成雪崩效應。

那麼Hystix的出現有什麼作用呢?它用來解決雪崩問題的手段有兩個:
1、線程隔離
2、服務熔斷

目前Hystix也已經不在更新,逐漸被resilience4j和sentinel代替。

線程隔離,服務降級

1、服務降級

優先保證核心服務,而非核心服務不可用或弱可用。

觸發Hystix服務降級的情況:

  • 線程池已滿
  • 請求超時

2、線程隔離

Hystrix爲每個依賴服務調用分配一個小的線程池,如果線程池已滿調用將被立即拒絕,默認不採用排隊.加速失敗判定時間。這樣一來,如果線程池已滿,或者請求超時,則會對該請求進行降級處理。

服務降級雖然會導致請求失敗,但是不會導致阻塞,而且最多會影響這個依賴服務對應的線程池中的資源,對其它服務沒有響應。

3、實踐

在上一篇文章項目的基礎上服務調用-Ribbon,我們做以下配置:

(1)在消費者模塊引入依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

(2)開啓熔斷

啓動類中加上註解

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

Spring就提供了一個組合註解:@SpringCloudApplication,用來替換 上面的三個註解。

@SpringCloudApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

(3)編寫降級邏輯

在controller中,添加降級邏輯

@RestController
public class TestServer {
//    private static final String ITEMSERVER_URL="http://localhost:8002";

    private static final String ITEMSERVER_URL="http://ITEM-SERVER";

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/test")
    @HystrixCommand(fallbackMethod = "queryUserByIdFallBack") //用來聲明一個降級邏輯的方法
    public CommonResult<Integer> test(){
        return restTemplate.getForObject(ITEMSERVER_URL+"/test",CommonResult.class);
    }

    public CommonResult<Integer> queryUserByIdFallBack(){
        return new CommonResult<>(500,"請求繁忙,請稍後再試!");
    }
}

在接口方法上面添加一個註解@HystrixCommand,用來聲明一個降級邏輯的方法。熔斷的降級邏輯方法必須跟正常邏輯方法保證:相同的參數列表和返回值聲明

我們重啓服務,當item-server提供正常服務時,訪問與之前一致,但是我們突然關掉item-server 會發現頁面返回了降級處理信息。
在這裏插入圖片描述
上面只是示範了一個方法,如果有多個方法需要進行降級處理,難道我們需要一個個的加上一個降級方法嗎?答案當然不是的,我們可以將我們的降級處理的註解加載類上,在類上制定一個全局熔斷的方法:

@RestController
@DefaultProperties(defaultFallback = "queryUserByIdFallBack") // 指定一個類的全局熔斷方法
public class TestServer {
//    private static final String ITEMSERVER_URL="http://localhost:8002";

    private static final String ITEMSERVER_URL="http://ITEM-SERVER";

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/test")
//    @HystrixCommand(fallbackMethod = "queryUserByIdFallBack") //用來聲明一個降級邏輯的方法
    @HystrixCommand //標記該方法需要熔斷
    public CommonResult<Integer> test(){
        return restTemplate.getForObject(ITEMSERVER_URL+"/test",CommonResult.class);
    }

    /**
     * 熔斷方法
     * 返回值要和被熔斷的方法的返回值一致
     * 熔斷方法不需要參數
     * @return
     */
    public CommonResult<Integer> queryUserByIdFallBack(){
        return new CommonResult<>(500,"請求繁忙,請稍後再試!");
    }
}

重新運行產生了和上面同樣的效果。

(4)設置超時

在之前的案例中,請求在超過1秒後都會返回錯誤信息,這是因爲Hystix的默認超時時長爲1,我們可以通過配置修改這個值:
我們可以通過hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds來設置Hystrix超時時間。該配置沒有提示。

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 # 設置hystrix的超時時間爲6000ms

服務熔斷

1、是什麼

熔斷機制的原理就像家裏的電路熔斷器,如果電路發生短路能力可熔斷電路。在分佈式系統中應用後,服務調用方可以自己進行判斷某些服務反應慢或者存在大量超時的情況時,能夠主動熔斷,防止整個系統被拖垮。

Hystrix可以實現彈性容錯,當情況好轉之後,可以自動重連。通過熔斷的方式可以將厚度請求直接拒絕掉,一段時間之後允許部分請求通過,如果調用成功則回到電路閉合狀態,否則繼續斷開。

熔斷狀態機3個狀態:

  • Closed:關閉狀態,所有請求都正常訪問。
  • Open:打開狀態,所有請求都會被降級。Hystix會對請求情況計數,當一定時間內失敗請求百分比達到閾值,則觸發熔斷,斷路器會完全打開。默認失敗比例的閾值是50%,請求次數最少不低於20次。
  • Half Open:半開狀態,open狀態不是永久的,打開後會進入休眠時間(默認是5S)。隨後斷路器會自動進入半開狀態。此時會釋放部分請求通過,若這些請求都是健康的,則會完全關閉斷路器,否則繼續保持打開,再次進行休眠計時。

2、實踐

我們在消費者模塊item-consumer的controller中加入邏輯:

 @GetMapping("/test")
//    @HystrixCommand(fallbackMethod = "queryUserByIdFallBack") //用來聲明一個降級邏輯的方法
    @HystrixCommand //標記該方法需要熔斷
    public CommonResult<Integer> test(@PathVariable("id")Long id){
        if (id == 1) {
            throw new RuntimeException("太忙了");
        }
        return restTemplate.getForObject(ITEMSERVER_URL+"/test",CommonResult.class);
    }

這樣只要參數爲1就一定失敗,準備兩個請求接口:
http://localhost:9002/test/1
http://localhost:9002/test/2
當我們瘋狂訪問id爲1的請求時(超過20次),就會觸發熔斷。斷路器會斷開,一切請求都會被降級處理。
此時你訪問id爲2的請求,會發現返回的也是失敗,而且失敗時間很短,只有幾毫秒左右:
在這裏插入圖片描述
默認的熔斷觸發要求較高,休眠時間窗較短,爲了測試方便,我們可以通過配置修改熔斷策略:

circuitBreaker.requestVolumeThreshold=10
circuitBreaker.sleepWindowInMilliseconds=10000
circuitBreaker.errorThresholdPercentage=50
  • requestVolumeThreshold:觸發熔斷的最小請求次數,默認20
  • errorThresholdPercentage:觸發熔斷的失敗請求最小佔比,默認50%
  • sleepWindowInMilliseconds:休眠時長,默認是5000毫秒
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章