Hystrix是一个实现断路器模式的库。什么是断路器模式呢?就像我们家庭中的电闸一样,如果有那一处出现意外,那么电闸就会立刻跳闸来防止因为这一处意外而引起更大的事故,直到我们确认处理完那一处意外后才可以再打开电闸。而Hystrix的存在就是为了预防程序中出现这种问题而导致程序不可用的情况。
比如说我们有三个微服务 A、B、C,其中A依赖于B,B依赖于C,如果这时候C出现了问题,那么就导致B不可用,紧接着A也不可用,更有可能导致整个系统不可用。我们接下来就来看看如何利用Hystrix预防这种情况
创建项目
首先我们复制一份cloud-demo-consumer项目,改名为cloud-demo-consumer-hystrix
引入Hystrix的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
application.xml不用变
spring:
application:
name: consumer-demo-hystrix
server:
port: 8090
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://root:root@localhost:8761/eureka
instance:
prefer-ip-address: true
application:
name: consumer-demo-hystrix
server:
port: 8090
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://root:root@localhost:8761/eureka
instance:
prefer-ip-address: true
CloudDemoConsumerApplication改名为CloudDemoConsumerHystrixApplication,并且它的注解应该是
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreake
@EnableEurekaClient
@EnableCircuitBreake
上方我们不认识的这个@EnableCircuitBreake注解就是表示开启断路器模式的注解
然后我们看一下controller
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/getUser/{id}")
@HystrixCommand(fallbackMethod = "getUserFallback")
public User getUser(@PathVariable Long id){
return restTemplate.getForObject("http://provider-demo/user/getUser/"+id,User.class);
}
public User getUserFallback(Long id) {
User user = new User();
user.setName("王五");
return user;
}
}
@RequestMapping("/user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/getUser/{id}")
@HystrixCommand(fallbackMethod = "getUserFallback")
public User getUser(@PathVariable Long id){
return restTemplate.getForObject("http://provider-demo/user/getUser/"+id,User.class);
}
public User getUserFallback(Long id) {
User user = new User();
user.setName("王五");
return user;
}
}
它相比较于原先的controller仅仅是多了一个@HystrixCommand(fallbackMethod = "getUserFallback")注解和一个方法,这个注解呢就是指定Hystrix在此方法超时时调用的方法。
测试
首先启动我们代表Eureka服务的项目,然后启动cloud-demo-provider项目,紧接着启动我们现在的项目。
项目启动以后我们打开浏览器访问localhost:8088/user/getUser/2的时候发现一切正常,网页上返回了张三这个用户。如果我们没有引入Hystrix的时候如果这时候把服务提供者停掉的话在访问会出现什么情况呢,是不是会报错,或者超时呀。
但是现在不一样了,我们引入了Hystrix,所以我们现在停掉提供者访问的时候会发现程序走了注解指定的fallbackMethod,也就是方法getUserFallBack,这个时候我们浏览器得到的结果是王五。
Hystrix默认的超时时间是1秒,也就是说它在等待服务提供者1秒后如果得不到结果的话就会认为提供者挂了,紧接着调用fallbackMethod。
这个时间其实我们可以控制,只需要在yml文件中配置一个属性就可以自定义这个时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 1000 #1000毫秒
.command.default.execution.isolation.thread.timeoutInMilliseconds: 1000 #1000毫秒
Feign的支持
接下来我们看一下Feign是怎么使用Hystrix,
这次我们改造cloud-demo-consumer-feign项目,项目名称改为cloud-demo-consumer-feign-hystrix,同样向上述 方式一样引入Hystrix的依赖,
接着 CloudDemoConsumerFeignApplication类名改为 CloudDemoConsumerFeignHystrixApplication,同样的加入@EnableCircuitBreaker注解
有一点不一样的地方是我们需要在yml文件中配置一下来开启Hystrix
feign.hystrix.enabled: true
.hystrix.enabled: true
这里controller中需要改造的不再是指定单个方法,而是指定接口的实现类
@FeignClient(name = "provider-demo", fallback = HystrixClientFallback.class)
name = "provider-demo", fallback = HystrixClientFallback.class)
来看一下这个实现类
@Component
public class HystrixClientFallback implements UserFeignClient {
@Override
public User getUser(Long id) {
User user = new User();
user.setName("王五");
return user;
}
}
public class HystrixClientFallback implements UserFeignClient {
@Override
public User getUser(Long id) {
User user = new User();
user.setName("王五");
return user;
}
}
这样的话如果接口中有多个方法的话我们就不必为每一个方法取指定了。
现在我们已经解决了服务提供者挂掉的事情了,但是有点不好的是,我们现在还不能知道服务提供者到底是咋挂的,要是能捕获到服务提供者
抛的异常就好了,其实Hystrix对这个是支持的,我们接下来看一下
fallbackFactory
UserFeignClient上方的注解需要变一下
@FeignClient(name = "provider-demo", fallbackFactory = HystrixClientFactory.class)
name = "provider-demo", fallbackFactory = HystrixClientFactory.class)
这次使用的是fallbackFactory这个属性,我们看一下它指定的这个类又是怎么实现的呢
@Component
public class HystrixClientFactory implements FallbackFactory<UserFeignClient> {
private static final Logger LOGGER = LoggerFactory.getLogger(HystrixClientFactory.class);
@Override
public UserFeignClient create(Throwable cause) {
HystrixClientFactory.LOGGER.info("the provider error is: {}", cause.getMessage());
return new UserFeignClient() {
@Override
public User getUser(Long id) {
User user = new User();
user.setName("王五");
return user;
}
};
}
}
public class HystrixClientFactory implements FallbackFactory<UserFeignClient> {
private static final Logger LOGGER = LoggerFactory.getLogger(HystrixClientFactory.class);
@Override
public UserFeignClient create(Throwable cause) {
HystrixClientFactory.LOGGER.info("the provider error is: {}", cause.getMessage());
return new UserFeignClient() {
@Override
public User getUser(Long id) {
User user = new User();
user.setName("王五");
return user;
}
};
}
}
我们可以看到,在这个create的工厂方法中,它的入参就是服务提供者的异常,得到了这个异常以后才会去做实现。这样是不是更加灵活了呢?
GitHub:https://github.com/shiyujun/spring-cloud-demo
如果对您有所帮助,请记得帮忙点一个star哦
本文出自https://zhixiang.org.cn,转载请保留。