继续我们 SpringCloud 之旅,签名谈及到了 Eureka实现服务注册与发现,有使用 Ribbon 实现负责均衡,使用 Feign 实现声明式 API 调用,接下来我们的处理下容错问题了。
Hystrix 介绍
Hystrixs 是Netflix 实现的一个开源延迟和容错库,我们发现一个请求出现问题的时候,需要能够自动处理这个错误,避免错误的扩展,比如 A 服务依赖 B,B 又依赖 C,如果此时 C 发生错误,首先影响到 B,B 又影响到 A,这样的雪崩效应最终导致整个系统故障。
Hystrix 通过如下几点实现:
- [x] 包裹请求
- [x] 跳闸机制
- [x] 资源隔离
- [x] 监控
- [x] 回退机制
- [x] 自我修复
集成 Hystrix
首先在 movie 的 pom 里面增加依赖
org.springframework.cloud
spring-cloud-starter-hystrix
1.3.5.RELEASE
其次在启动类上增加@EnableCircuitBreaker和@EnableHystrix,为项目启用断路器支持
再次在 control 里面修改对应的方法,使得某个方法不能正常使用的时候,使用默认返回值。
@GetMapping("/user/{id}")
@HystrixCommand(fallbackMethod="findByUserIdFallback")
public User findByUserId(@PathVariable Integer id){
return restTemplate.getForObject("http://MS-SIMPLE-PROVIDER-USER/user/find?id="+id, User.class);
//return userFeignClient.findById(id);
}
//这个方法是上面的方法发生错误的时候调用
public User findByUserIdFallback(Integer id){
User user=new User();
user.setId(0);
user.setName("NULL");
user.setNickName("默认用户");
user.setPassword("");
user.setLastLoginDt(null);
return user;
}
在原先的方法上增加@HystrixCommand(fallbackMethod="findByUserIdFallback"),使得错误发生的时候能够调用下面的回调方法,回调方法参数与上面的一模一样。
@HystrixCommand该注解的里面参数非常多,需要的时候可以查阅文档,包括超时多久进行回调,线程池配置参数等。
以上是一个基本的演示配置,接下来我们先运行试试看,开启 eureka,开启 user,开启 movie,在浏览器浏览 http://localhost:8010/user/1 返回的就是 我们默认的用户信息,因为之前的user模块需要用户密码访问,我们这里面直接访问是不行的。
如果发现
com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect]: Factory method 'hystrixCommandAspect' threw exception; nested exception is java.lang.NoClassDefFoundError: org/aspectj/lang/JoinPoint
需要增加
org.aspectj
aspectjweaver
1.8.6
Feign 使用 Hystrix
上面的例子没有使用 Feign 的,现在我们来用 Feign 使用 Hystrix。不废话,直接来干货吧。
因为 feign 是接口,我们需要在接口做一些动作。
首先使用之前的 FeignClient 接口方式来,对项目进行修正下,注释掉 movie 项目 Controller 的userUserFeignClient和adminUserFeignClient,以及相关的代码,然后修改映射方法为
@GetMapping("/user/{id}")
// @HystrixCommand(fallbackMethod="findByUserIdFallback")
public User findByUserId(@PathVariable Integer id){
//return restTemplate.getForObject("http://MS-SIMPLE-PROVIDER-USER/user/find?id="+id, User.class);
return userFeignClient.findById(id);
}
接下来在接口UserFeignClient 做如下修改
@FeignClient(name="MS-SIMPLE-PROVIDER-USER",fallback=UserFeignClientFallBack.class)
public interface UserFeignClient {
@RequestMapping(value="/user/find?id={id}",method=RequestMethod.GET)
public User findById(@PathVariable("id") Integer id);
}
这里需要一个UserFeignClientFallBack类实现了该接口
@Component
public class UserFeignClientFallBack implements UserFeignClient{
@Override
public User findById(Integer id) {
User user=new User();
user.setId(0);
user.setName("NULL");
user.setNickName("默认用户");
user.setPassword("");
user.setLastLoginDt(null);
return user;
}
}
这些操作完成后重启 movie,访问 http://localhost:8010/user/1
同样得到
使用回调工厂检查原因
改变不是太大,在接口的地方回调的是一个工厂类
package cn.ts.ms.movie.feign;
import org.springframework.stereotype.Component;
import cn.ts.ms.movie.model.User;
import feign.hystrix.FallbackFactory;
@Component
public class UserFeignClientFactory implements FallbackFactory {
@Override
public UserFeignClient create(Throwable cause) {
return new UserFeignClient(){
@Override
public User findById(Integer id) {
//这里打印输出错误
System.out.println(cause);
User user=new User();
user.setId(0);
user.setName("NULL");
user.setNickName("默认用户-Factory");
user.setPassword("");
user.setLastLoginDt(null);
return user;
}
};
}
}
修改接口出的 fallback 类@FeignClient(name="MS-SIMPLE-PROVIDER-USER",fallbackFactory=UserFeignClientFactory.class)
,注意是fallbackFactory
重启运行 movie,效果一样
全局禁用Hystrix
只须在application.yml中配置feign.hystrix.enabled=false即可。
Hystrix线程隔离策略与传播上下文
hystrix的隔离策略有两种:线程隔离和信号量隔离
线程隔离(THREAD):使用该方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中的线程数量的限制。
信号量隔离(SEMAPHORE):使用该方式,HystrixCommand将会在调用线程上执行,开销相对较小,并发请求受信号量个数的限制。
hystrix中默认并且推荐使用线程隔离,因为这种方式有一个除网络超时以外的额外保护层。
一般来说,只有当调用负载非常高时(如:每个实例每秒调用数百次)才需要使用信号量隔离,因为这种场景下使用线程隔离开销会比较大。信号量隔离一般仅适用于非网络调用的隔离。
可使用execution.isolation.strategy属性指定隔离策略。
使用 Hystrix Dashboard 来监控数据
首先引入依赖
org.springframework.cloud
spring-cloud-starter-hystrix-dashboard
1.3.5.RELEASE
启动类增加注解 @EnableHystrixDashboard
重新启动 movie,浏览 http://localhost:8010/hystrix
出现
基本配置下进入