Spring Cloud Hystrix實現(xiàn)了斷路器、線程隔離等一系列服務(wù)保護措施采驻,它也是基于Netflix的開源框架Hystrix實現(xiàn)的墓毒,該框架的目標是通過控制那些訪問遠程系統(tǒng)动羽、服務(wù)和第三方庫的節(jié)點,從而對延遲和故障提供更強大的容錯能力镣典。Hystrix具備服務(wù)降級兔毙、服務(wù)熔斷、線程和信號隔離兄春、請求緩存澎剥、請求合并以及服務(wù)監(jiān)控等強大功能。
快速入門
-在服務(wù)消費者工程的pom.xml中添加spring-cloud-starter-hystrix依賴
<dependency>
<groupId>org.springframework.cloud<groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
-在消費者工程的主類ConsumerApplication中使用@EnableCircuitBreaker注解開啟斷路器功能赶舆。
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String args[]) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
-改造服務(wù)消費方式哑姚,新增HelloService類,注入RestTemplate實例芜茵,然后叙量,將在ConsumerController中對RestTemplate的使用遷移到HelloService函數(shù)中,最后在HelloService函數(shù)上增加@HystrixCommand注解來指定回調(diào)方法:
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod="helloFallback")
public String helloService() {
return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
}
public String helloFallback() {
return "error";
}
}
-修改ConsumerController類九串,注入上面實現(xiàn)的HelloService實例绞佩,并在helloConsumer中進行調(diào)用
@RestController
public class ConsumerController {
@Autowired
HelloService helloService;
@RequestMapping(value="/ribbon-consumer", method=RequestMethod.GET)
public String helloConsumer() {
return helloService.helloService();
}
}
原理分析
工作流程
1寺鸥、創(chuàng)建HystrixCommand或者HystrixObservableCommand對象
-HystrixCommand:用在依賴的服務(wù)返回單個操作結(jié)果的時候。
-HystrixObservableCommand:用在依賴的服務(wù)返回多個操作結(jié)果的時候品山。
2胆建、命令執(zhí)行
Hystrix共有四種命令的執(zhí)行方式,HystrixCommand實現(xiàn)了兩種執(zhí)行方式肘交,分別為:
-execute():同步執(zhí)行笆载,從依賴的服務(wù)返回一個單一的結(jié)果對象,或是在發(fā)生錯誤的時候拋出異常
-queue():異步執(zhí)行酸些,直接返回一個Future對象宰译,其中包含了服務(wù)執(zhí)行結(jié)束時要返回的單一結(jié)果的對象
R value = command.execute();
Future<R> fValue = command.queue();
HystrixObservableCommand實現(xiàn)了另外兩種執(zhí)行方式
-observe():返回Observable對象檐蚜,它代表了操作的多個結(jié)果魄懂,它是一個HotObservable。
-toObservable():同樣會返回Observable對象闯第,也代表了操作的多個結(jié)果市栗,但它返回的是一個Cold Observable。
Observable<R> ohValue = command.observe();
Observable<R> ocValue = command.toObservable();
3咳短、結(jié)果是否被緩存
4填帽、斷路器是否打開
5、線程池/請求隊列/信號量是否占滿
依賴隔離
Hystrix使用“艙壁隔離”模式實現(xiàn)線程池的隔離咙好,它會為每一個依賴服務(wù)創(chuàng)建一個獨立的線程池篡腌,這樣就算某個依賴服務(wù)出現(xiàn)延遲過高的情況,也只是對該依賴服務(wù)的調(diào)用產(chǎn)生影響勾效,而不會陀滿其他的依賴服務(wù)嘹悼。
使用詳解
Hystrix的核心注解是@HystrixCommand,通過它創(chuàng)建了HystrixCommand的實現(xiàn),同時利用fallback屬性指定了服務(wù)降級的實現(xiàn)方法层宫。
創(chuàng)建請求命令
Hystraix命令就是HystrixCommand杨伙,用于封裝具體的依賴服務(wù)調(diào)用邏輯。通過繼承的方式來實現(xiàn)
public class UserCommand extends HystrixCommand<User> {
private RestTemplate restTemplate;
private Long id;
public UserCommand(Setter setter, RestTemplate restTemplate萌腿, Long id) {
super(setter);
this.restTemplate = restTemplate;
this.id = id;
}
@Override
protected User run() {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
}
}
-同步執(zhí)行:User u = new UserCommand(restTemplate, 1L).execute();
-異步執(zhí)行:Future<User> futureUser = new UserFuture(restTemplate,1L).queue();異步執(zhí)行的時候限匣,可以通過對返回的futureUser調(diào)用get方法來獲得結(jié)果。
另外毁菱,也可以通過@HystrixCommand注解來實現(xiàn)Hystrix命令
public class UserService {
@Autowired
private RestTemplate restTemplate米死;
@HystrixCommand
public User getUserById(Long id) {
return restTemplate.getForObject()
}
}
雖然@HystrixCommand注解可以定義Hystrix命令的實現(xiàn),但是如上定義的getUserById方式只是同步執(zhí)行的實現(xiàn)贮庞,若要實現(xiàn)異步執(zhí)行則還需要另外定義
@HystrixCommand
public Future<User> getUserByIdAsync(final String id) {
return new AsyncResult<User> () {
@Override
public User invoke() {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}",User.class,id);
}
}
}
在使用@HystrixCommand注解實現(xiàn)響應(yīng)式命令時哲身,可以通過observableExecutionMode參數(shù)來控制使用observe()還是toObservable()的執(zhí)行方式。該參數(shù)有兩種設(shè)置方式贸伐。
-@HystrixCommand(observableExecutionMode=ObservableExecutionMode.EAGER);EAGER是該參數(shù)的模式值勘天,表示toObserve()執(zhí)行方式
-@HystrixCommand(observableExecutionMode=ObservableExecutionMode.LAZY);表示使用toObservable()執(zhí)行方式。
定義服務(wù)降級
fallback是Hystrix命令執(zhí)行失敗時使用的后備方法,用來實現(xiàn)服務(wù)的降級處理邏輯脯丝。在HystrixCommand中可以通過重載getFallback()方法來實現(xiàn)服務(wù)降級邏輯商膊,Hystrix會在run()執(zhí)行過程中出現(xiàn)錯誤、超時宠进、線程池拒絕晕拆、斷路器等情況時,執(zhí)行g(shù)etFallback()方法內(nèi)的邏輯
public class UserCommand extends HystrixCommand<User> {
private RestTemplate restTemplate;
private Long id;
public UserCommand(Setter setter, RestTemplate restTemplate,Long id) {
super(setter);
this.restTemplate = restTemplate材蹬;
this.id = id;
}
@Override
protected User run() {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}",User.class,id);
}
@Override
protected User getFallback() {
return new User();
}
}
使用注解實現(xiàn)降級服務(wù)只需要使用@HystrixCommand中的fallbackMethod參數(shù)來指定具體的服務(wù)降級實現(xiàn)方法
public class UserService {
@Autowired
private RestTemplate restTemplate实幕;
@HystrixCommand(fallbackMethod="defaultUser")
public User getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}",User.class,id)
}
public User defaultUser() {
return new User();
}
}
在使用注解定義服務(wù)降級邏輯時,我們需要將具體的Hystrix命令與fallback的實現(xiàn)函數(shù)定義在同一個類中堤器,并且fallbackMethod的值必須與實現(xiàn)fallback方法的名字相同昆庇。由于必須定義在一個類中,所以對于fallback的訪問修飾符沒有特定的要求闸溃,定義為private整吆、protected、public均可辉川。
異常處理
異常傳播
當(dāng)HystrixCommand實現(xiàn)的run()方法拋出異常時表蝙,這些異常會被Hystrix認為是命令執(zhí)行失敗并觸發(fā)服務(wù)降級的處理邏輯。
使用注解配置實現(xiàn)Hystrix命令時乓旗,它還支持忽略指定異常類型功能
@HystrixCommand(ignoreExceptions={BadRequestException.class})
public User getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}",User.class,id);
}
異常處理
命令名稱府蛇、分組以及線程池劃分
以繼承方式實現(xiàn)的Hystrix命令使用類名作為作為默認名稱,我們也可以在構(gòu)造函數(shù)中通過Setter靜態(tài)類來設(shè)置
public UserCommand() {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("Groupname")).andCommandKey(HystrixCommandKey.Factory.asKey("CommandName")))
}
使用注解的時候設(shè)置命令名稱屿愚、分組以及線程池
@HystrixCommand(commandKey="getByUserId", groupKey="UserGroup", threadPoolKey="getUserByIdThread")
public User getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}",User.class,id);
}
請求緩存
開啟請求緩存的功能:在實現(xiàn)HystrixCommand或HystrixObservableCommand時汇跨,通過重載getCacheKey()方法來開啟請求緩存。
清理失效緩存功能:在Hystrix中渺鹦,通過HystrixRequestCache.clear()方法進行緩存清理扰法。
工作原理
嘗試獲取請求:Hystrix命令在執(zhí)行前會根據(jù)之前提到的isRequestCachingEnabled方法來判斷當(dāng)前命令是否啟用了請求緩存。如果開啟了請求緩存且重寫了getCacheKey方法毅厚,并返回了一個非null的緩存Key值塞颁,那么就使用getCacheKey返回的Key值調(diào)用HystrixRequestCache的get(String cacheKey)獲取緩存的HystrixCachedObservable對象。
將請求結(jié)果加入緩存
-設(shè)置請求緩存:添加@CacheResult注解吸耿,Hystrix會將結(jié)果加入請求緩存中祠锣,而它的緩存Key值會使用所有的參數(shù)
@CacheResult
@HystrixCommand
public User getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}",User.class,id);
}
-定義緩存key:可以使用@CacheResult和@CacheRemove注解的cacheKeymethod方法來指定具體的生成函數(shù);也可以通過使用@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;
}
使用@CacheKey注解實現(xiàn)方式更簡單伴网,但是它的優(yōu)先級比cacheKeyMethod的優(yōu)先級低,如果已經(jīng)使用了cacheKeyMethod指定了緩存key的生成函數(shù)妆棒,那么@CacheKey注解將不會生效
@CacheResult
@HystrixCommand
public User getUserById(@CacheKey("id") Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}",User.class,id);
}
@CacheKey注解除了可以指定方法參數(shù)作為緩存Key之外澡腾,它還允許訪問參數(shù)對象的內(nèi)部屬性作為緩存Key
@CacheKey
@HystrixCommand
public User getUserById(@CacheKey("id") User user) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}",User.class,user.getId())
}
-緩存清理沸伏,通過@CacheRemove注解來實現(xiàn)失效緩存的清理
@CacheResult
@HystrixCommand
public User getUserById (@CacheKey ("id") Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1} ", User. class);
}
@CacheRemove(commandKey="getUserById")
@HystrixCommand
public void update(@CacheKey("id") User user) {
return restTemplate.postForObject("http://USER-SERVICE/users",user,User.class);
}
@CacheRemove注解的commandKey屬性必須要指定的,它用來指明需要使用請求緩存的請求命令
請求合并
屬性詳解
command屬性
command屬性主要用來控制的是HystrixCommand命令的行為动分,主要有五種不同類型的屬性配置
-execution配置毅糟,主要用來控制HystrixCommand.run()的執(zhí)行
-fallback配置,主要用于控制HystrixCommand.getFallback()的執(zhí)行澜公,這些屬性同時適用于線程池的信號量的隔離策略姆另。
-circuitBreaker配置,主要用于HystrixCircuitBreaker斷路器的行為
-metrics配置坟乾,與HystrixCommand和HystrixObservableCommand執(zhí)行中捕獲的指標信息有關(guān)迹辐。
-requestContext配置,與HystrixCommand使用的HystrixRequestContext的設(shè)置甚侣。