服務容錯保護 Spring Cloud Hystrix
在微服務架構中,系統被拆分成了很多服務單元,各個單元間通過服務注冊與訂閱的方式互相依賴,由于每個單元都在不同的行程中運行,依賴通過遠程呼叫的方式執行,這樣就有可能因為網路或依賴服務自身問題出現呼叫故障或延遲,同時也會導致呼叫方的對外服務也出現延遲,若呼叫方的請求不斷增加,會因等待出現故障的依賴方回應形成任務積壓,最終導致自身服務的癱瘓及整個系統的癱瘓,為了解決這樣的問題,產生了斷路器等一系列的服務保護機制,
在分布式架構中,當某個服務單元發生故障之后,通過斷路器的故障監控,向呼叫方回傳一個錯誤回應,而不是長時間的等待,這樣就不會使得執行緒因呼叫故障服務被長時間占用不釋放,避免故障在分布式系統中的蔓延,
Spring Cloud Hystrix實作了斷路器,執行緒隔離等一系列的服務保護功能,它基于Netflix的開源框架Hystrix實作,通過控制訪問遠程系統,服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力,Hystrix具備服務降級,服務熔斷,執行緒和信號隔離,請求快取,請求合并以及服務監控等強大的功能,
1. 快速集成 Spring Cloud Hystrix
1.1 基礎服務
- eureka-server工程:服務注冊中心
- hello-service工程:多個服務提供者服務單元
- ribbon-service工程:使用Spring Cloud Ribbon實作的服務消費者
1.2 集成 Spring Cloud Hystrix
在ribbon-service工程中添加依賴:spring-cloud-starter-hystrix
在ribbon-service工程主類上使用
@EnableCircuitBreaker注解開啟斷路器功能在ribbon-service消費服務對應的Service函式上增加
@HystrixCommand注解指定回呼方法public String helloFallback() { // fallback method return "error"; } @Override @HystrixCommand(fallbackMethod = "helloFallback") public void test() { // do something return "success"; }
2. Spring Cloud Hystrix使用詳解
2.1 創建請求命令
通過使用@HystrixCommand注解可以實作請求的同步執行與異步執行,更優雅的實作Hystrix命令的定義,
-
同步執行
@Autowired private RestTemplate restTemplate; @HystrixCommand public User getUserById(Long id) { return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id); } -
異步執行
@HystrixCommand public Future<User> getUserByIdAsync(final Long id) { return new AsyncResult<User>() { @Override public User invoke() { return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id); } }; }
除同步執行與異步執行外,還可以將HystrixCommand通過Observable來實作回應式執行方式,注解實作依然是使用@HystrixCommand,在使用@HystrixCommand注解實作回應式命令時,可以通過observableExecutionMode引數控制使用observe()還是toObservable()的執行方式,
@HystrixCommand
public Observable<User> getUserById(final Long id) {
return Observable.create(new Observable.OnSubscribe<User>() {
@Override
public void call(Subscriber<? super User> observer) {
try {
if (!observer.isUnsubscribed()) {
User user = restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
observer.onNext(user);
observer.onCompleted();
}
} catch (Exception exception) {
observer.onError(exception);
}
}
});
}
observableExecutionMode引數設定
-
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER)
EAGER表示使用observe()執行方式
-
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.LAZY)
LAZY表示使用toObservable()執行方式
2.2 服務降級
注解實作通過使用@HystrixCommand注解中的fallbackMethod引數指定具體的服務降級方法
2.3 例外處理
-
例外傳播
在HystrixCommand實作的run()方法中拋出例外時,除了
HystrixBadRequestException外,其它的例外均會被Hustrix認為命令執行失敗并處罰服務降級的處理邏輯,HystrixCommand支持忽略指定例外型別的功能,通過使用
@HystrixCommand注解中的ignoreException引數@HystrixCommand(ignoreException = {BadRequestException.class}) public User getUserById(Long id) { return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id); } -
例外獲取
Hystrix命令因為例外進入服務降級邏輯后,往往需要對不同的例外做針對性處理,需要獲取當前拋出的例外,
通過在
@HystrixCommand注解的fallbackMethod實作方法的引數中增加Throwable e獲取當前拋出的例外物件,public User helloFallback(Long id, Throwable e) { // fallback method return null; } @Override @HystrixCommand(fallbackMethod = "helloFallback") public User getUserById(Long id) { throw new RuntimeException("get user failed"); }
2.4 命令名稱,分組及執行緒池劃分
通過設定命令組,Hystrix會根據組來組織和統計命令的告警和儀表盤等資訊,注解實作只需要設定@HystrixCommand注解的commandKey,groupKey及threadPoolKey屬性即可,它們分別表示命令分組,分組及執行緒池劃分,
@HystrixCommand(commandKey="getUserById", groupKey="UserGroup", threadPoolKey="getUserByIdThread")
public User getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
}
2.5 請求快取
? Hystrix中提供了請求快取的功能,可以方便的開啟和使用請求快取來優化系統,達到減輕高并發時的請求執行緒消耗,降低請求回應時間的效果,
? 注解實作請求快取,請求快取的注解:
| annotation | description | attributes |
|---|---|---|
| @CacheResult | 標識請求命令結果應被快取 必須與 @HystrixCommand結合使用 |
cacheKeyMethod |
| @CacheRemove | 使請求命令的快取失效 失效的快取根據Key決定 |
commandKey cacheKeyMethod |
| @CacheKey | 在請求命令的引數上標記,使其作為快取的Key值 若沒有標記則使用所有引數,若同時還使用了 @CacheResult和@CacheRemove注解的cacheKeyMethod方法指定快取Key的生成,則該注解失效 |
value |
-
設定快取請求
使用
@CacheResult注解為請求命令開啟快取功能,當該依賴服務被呼叫并回傳實體物件時,由于該方法被@CacheResult注解修改,所以Hystrix會將該結果置入請求快取中,而它的快取Key值會使用所有引數,@CacheResult @HystrixCommand public User getUserById(Long id) { return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id); } -
定義快取Key
當使用注解定義快取請求時,若要為請求命令指定具體的快取Key生成規則,可以使用
@CacheResult和@CacheRemove注解的cacheKeyMethod方法指定具體的生成函式,也可以通過使用@CacheKey注解在方法的引數中指定用于組裝快取Key的元素,@CacheKey注解還允許訪問引數物件的內部屬性作為快取Key,@CacheResult(cacheKeyMethod = "getUserByIdCacheKey") @HystrixCommand public User getUserById(Long id) { return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id); } private Long getUserByIdCacheKey(Long id) { return id; }@CacheResult @HystrixCommand public User getUserById(@CacheKey("id") Long id) { return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id); } @CacheResult @HystrixCommand public User getUserByPhone(@CacheKey("phone") User user) { return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id); } -
快取清理
使用
@CacheResult注解將請求結果置入Hystrix的請求快取中,若改內容呼叫了update操作進行了更新,那么此時請求快取與實際結果不一致,因此需要在update操作上對失效的快取進行清理,可以通過@CacheRemove注解實作失效快取的清理,需要注意@CacheRemove注解的commandKey屬性必須制定,它用來指明使用請求快取的請求命令,只有通過該屬性,Hystrix才能找到正確的請求命令快取的位置,@CacheResult @HystrixCommand public User getUserById(@CacheKey("id") Long id) { return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id); } @CacheRemove(commandKey = "getUserById") @HystrixCommand public void update(@CacheKey("id") User user) { return restTemplate.postForObject("http://USER-SERVICE/users", user, User.class); }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/89866.html
標籤:Java
