接上篇《16.超时机制,断路器模式简介》 Spring Cloud版本为Finchley.SR2版
上一篇我们介绍了超时机制以及断路器模式,并且引申出了Spring Cloud的断路器组件Hystrix。本篇我们来学习Hystrix的基础知识。
本部分官方文档:https://cloud.spring.io/spring-cloud-static/Finchley.SR2/single/spring-cloud.html#_circuit_breaker_hystrix_clients
Hystrix的读音是 [hIst'rIks](嗨斯捶克斯~哈哈哈),在英文里面是豪猪的意思,大概是下面这个:
豪猪身上有很多刺,这些刺可以很好的保护自己,所以开发团队给这个保护组件起名为Hystrix。
首先我们来看一下Spring Cloud官方文档对Hystrix的介绍:
Netflix创建了一个名为Hystrix的库,实现了断路(熔断)器的模式。一般在微服务架构通常有多层服务调用:
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝,这里默认服务5秒失败20次就认为服务异常),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩的发生:
拥有一个断路器可以停止级联故障,并且给予失败的服务有自我恢复的时间。回退机制中的备选响应(FallBack)可以是另外一个服务,或者是一个静态数据,当然也可以是一个空值。
我们需要引入Hystrix组件的话,在pom文件中添加spring-cloud-starter-netflix-hystrix的依赖即可:
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
然后在启动类上添加@EnableCircuitBreaker注解,引入断路器:
@SpringBootApplication
@EnableCircuitBreaker
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
然后在需要使用断路器进行备选响应的服务上,添加@HystrixCommand注解,并指定服务异常之后回退的具体方法(fallbackMethod):
@Component
public class StoreIntegration {
@HystrixCommand(fallbackMethod = "defaultStores")
public Object getStores(Map parameters) {
//do stuff that might fail
}
public Object defaultStores(Map parameters) {
return /* something useful */;
}
}
当调用getStores服务异常的时候,就会自动进行服务降级,调用“fallbackMethod”参数指定的回退方法。
上面例子看起来还是比较简单的,而实际上Hystrix的使用也是这么方便的。那么为什么引入@HystrixCommand就能实现断路器模式呢?
首先我们要知道,@HystrixCommand是由名为“javanica”的Netflix contrib 库提供,什么是“javanica”呢?
javanica是Netflix contrib下的一个子项目,其在github上的仓库地址为:
https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica
我们可以在其github首页的README.md公告中了解到,javanica工程实现了一个HystrixCommandAspect切面类,使用AOP切面机制,处理请求服务的前置和后置逻辑:
...
...
然后就可以直接使用@HystrixCommand进行服务的直接访问了:
public class UserService {
...
@HystrixCommand
public User getUserById(String id) {
return userResource.getUserById(id);
}
}
...
那么,说到底,@HystrixCommand其实并不是Hystrix的原生注解(其在com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand包下),而是“javanica”项目对原来的Hystrix进行了改进和封装,使开发者更方便的使用Hystrix。
我们可以直接去Hystrix的github首页,然后在其wiki上(https://github.com/Netflix/Hystrix/wiki/How-To-Use)看看原始的Hystrix是如何使用的:
继承HystrixCommand
public class CommandHelloWorld extends HystrixCommand {
private final String name;
public CommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected String run() {
// a real example would do work like a network call here
return "Hello " + name + "!";
}
@Override
protected String getFallback() {
return "Hello Failure " + name + "!";
}
}
或者是继承HystrixObservableCommand
public class CommandHelloWorld extends HystrixObservableCommand {
private final String name;
public CommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected Observable construct() {
return Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> observer) {
try {
if (!observer.isUnsubscribed()) {
// a real example would do work like a network call here
observer.onNext("Hello");
observer.onNext(name + "!");
observer.onCompleted();
}
} catch (Exception e) {
observer.onError(e);
}
}
} ).subscribeOn(Schedulers.io());
}
/**
* fallback方法的写法,覆写resumeWithFallback方法
* 当调用出现异常时,会调用该降级方法
*/
@Override
public Observable resumeWithFallback() {
return Observable.create(new OnSubscribe() {
@Override
public void call(Subscriber super String> observer) {
try {
if (!observer.isUnsubscribed()) {
observer.onNext("Hello Failure");
observer.onNext(name + "!");
observer.onCompleted();
}
} catch (Exception e) {
observer.onError(e);
}
}
}).subscribeOn(Schedulers.io());
}
}
调用的时候调用execute方法即可:
String s = new CommandHelloWorld("World").execute();
也可以使用queue方法异步执行,使用Future
Future fs = new CommandHelloWorld("World").queue();
String s = fs.get();
我们要感谢“javanica”项目然我们如此方便的使用Hystrix。
要配置@Hystrixcommand注解,可以使用其commandproperties属性。有关commandproperties属性的相关知识我们后面会详细介绍。
我们在之前的microserver-consumer-movie工程上进行Hystrix的测试。首先在其pom.xml文件中添加Hystrix的依赖:
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
然后在启动类MicroserverSimpleConsumerMovieApplication类添加@EnableCircuitBreaker注解:
package com.microserver.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import com.microserver.config.TestConfiguration;
@SpringBootApplication
@EnableCircuitBreaker
@EnableFeignClients
@RibbonClient(name = "microserver-provider-user",configuration=TestConfiguration.class )
public class MicroserverSimpleConsumerMovieApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserverSimpleConsumerMovieApplication.class, args);
}
}
我们还记得之前在MovieController中编写了“/movie/{id}”的服务,方法为findUserById,
@RestController
public class MovieController {
//上面代码省略...
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/movie/{id}")
public User findUserById(@PathVariable Long id){
return userFeignClient.findById(id);
}
//下面代码省略...
}
我们准备对这个服务进行异常断路和降级的操作。首先给findUserById方法添加@Hystrixcommand注解,并且配置其服务降级退回方法为“findUserByIdFallback”:
@RestController
public class MovieController {
//上面代码省略...
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/movie/{id}")
@HystrixCommand(fallbackMethod = "findUserByIdFallback")
public User findUserById(@PathVariable Long id){
return userFeignClient.findById(id);
}
public User findUserByIdFallback(){
User user = new User();
user.setId(0L);
return user;
}
//下面代码省略...
}
fallbackMethod对应的方法,其中的参数和返回值一定要和原来的服务相同。
然后重启movie工程、user工程以及eureka Server工程:
在eureka Server首页可以看到两个工程都是UP状态:
此时我们通过movie工程的“movie/{id}”服务来获取用户信息,请求id为1的用户信息:
可以看到此时反馈是正常的。这里要注意的是,我们首次访问的时候有可能会进降级方法,这是因为Hystrix默认的服务超时时间是1秒,这个时间太短了,对响应时间稍微长一点,或者网络速度稍微差一点的服务是不友好的。所以我们要在配置文件application.yml添加Hystrix的服务超时时间:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
这里我们设置默认的服务超时时间为5秒(5000毫秒)。
下面我们模拟服务崩溃的情况,我们将user服务停止,这样movie去请求user的时候会失败的:
此时我们重新请求“movie/{id}”服务,可以看到返回的正是我们的降级服务findUserByIdFallback返回的Id为0的数据:
我们也可以观察到,浏览器在请求时也没有加载的迹象,我们打断点的时候,可以看到此时服务会在进入findUserById后,直接进findUserByIdFallback方法,而不会再去尝试请求原有服务。
一个简单的配置和演示就到这里,后面的博文会对Hystrix进行进一步的剖析。
参考:《51CTO学院Spring Cloud高级视频》
https://blog.csdn.net/syc000666/article/details/96097567
https://blog.csdn.net/zjl_pcw/article/details/87175077
转载请注明出处:https://blog.csdn.net/acmman/article/details/100523411