在微服務架構中,系統被拆分紅了不少服務單元,各個單元間經過服務註冊與訂閱的方式互相依賴。因爲每一個單元都在不一樣的進程中運行,依賴經過遠程調用的方式執行,這樣就有可能由於網絡或依賴服務自身問題出現調用故障或延遲,同時也會致使調用方的對外服務也出現延遲。若調用方的請求不斷增長,會因等待出現故障的依賴方響應造成任務積壓,最終致使自身服務的癱瘓及整個系統的癱瘓。爲了解決這樣的問題,產生了斷路器等一系列的服務保護機制。java
在分佈式架構中,當某個服務單元發生故障以後,經過斷路器的故障監控,向調用方返回一個錯誤響應,而不是長時間的等待。這樣就不會使得線程因調用故障服務被長時間佔用不釋放,避免故障在分佈式系統中的蔓延。
Spring Cloud Hystrix實現了斷路器,線程隔離等一系列的服務保護功能。它基於Netflix的開源框架Hystrix實現,經過控制訪問遠程系統,服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力。Hystrix具有服務降級,服務熔斷,線程和信號隔離,請求緩存,請求合併以及服務監控等強大的功能。spring
- eureka-server工程:服務註冊中心
- hello-service工程:多個服務提供者服務單元
- ribbon-service工程:使用Spring Cloud Ribbon實現的服務消費者
在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"; }
經過使用@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參數設置ide
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER)
EAGER表示使用observe()執行方式
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.LAZY)
LAZY表示使用toObservable()執行方式
註解實現經過使用@HystrixCommand
註解中的fallbackMethod參數指定具體的服務降級方法
異常傳播
在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"); }
經過設置命令組,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); }
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); }