前言
在前面呢我们有介绍什么是Hystrix
,以及Hystrix
的作用。那么本篇文章呢我们将结合代码,来演示如何利用Hystrix
来现实服务的熔断和降级。
集成Hystrix
首先在spring-cloud-examples
的基础上新建一个子module
——spring-cloud-hystrix
,然后在spring-cloud-hystrix
的基础上再分别建立consumer-hystrix
和consumer-feign-hystrix
两个module
如图所示:
分别将spring-cloud-consumer-user-feign
和spring-cloud-consumer-user
的代码、配置文件copy至刚刚新建的项目consumer-hystrix
、consumer-feign-hystrix
。
修改consumer-hystrix
pom文件,增加hystrix依赖:
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
完整pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-hystrix</artifactId>
<groupId>com.chaytech</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumer-hystrix</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.chaytech</groupId>
<artifactId>spring-cloud-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.chaytech</groupId>
<artifactId>spring-cloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ribbon相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</project>
修改consumer-hystrix
中的UserController
,增加一个方法上并在方法加上@HystrixCommand
注解,标示此方法带容错机制。一旦调用服务方法失败,会自动调用@HystrixCommand
标注好的fallbackMethod
调用类中的指定方法:
@GetMapping("/consumer/user/getUserHystrix/{id}")
// 一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
@HystrixCommand(fallbackMethod = "userHystrixFallback")
public UserEntity getUserHystrix(@PathVariable("id") Integer id) {
Logger.info("getUserHystrix thread name is {}", Thread.currentThread().getName());
try{
Thread.sleep(1500);
}catch (Exception e){
}
return userApi.getUser(id);
}
/**
* 熔断降级回退方法
*
* 注意此方法返回值和参数需与目标方式一致
*
* @param id
* @return
*/
private UserEntity userHystrixFallback(Integer id){
UserEntity userEntity = new UserEntity("系统繁忙,请稍后再试!");
return userEntity;
}
完整UserController
:
@RestController
public class UserController {
private Logger Logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserApi userApi;
@PostMapping("/consumer/user/add")
public boolean add(UserEntity user) {
return userApi.createUser(user);
}
@GetMapping("/consumer/user/getUser/{id}")
public UserEntity getUser(@PathVariable("id") Integer id) {
Logger.info("getUser thread name is {}", Thread.currentThread().getName());
try{
Thread.sleep(1500);
}catch (Exception e){
}
return userApi.getUser(id);
}
@GetMapping("/consumer/user/listUser")
public List<UserEntity> list() {
return userApi.listUser();
}
@GetMapping("/consumer/user/getUserHystrix/{id}")
// 一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
@HystrixCommand(fallbackMethod = "userHystrixFallback")
public UserEntity getUserHystrix(@PathVariable("id") Integer id) {
Logger.info("getUserHystrix thread name is {}", Thread.currentThread().getName());
try{
Thread.sleep(1500);
}catch (Exception e){
}
return userApi.getUser(id);
}
/**
* 熔断降级回退方法
*
* 注意此方法返回值和参数需与目标方式一致
*
* @param id
* @return
*/
private UserEntity userHystrixFallback(Integer id){
UserEntity userEntity = new UserEntity("系统繁忙,请稍后再试!");
return userEntity;
}
}
修改consumer-hystrix
启动类,增加@EnableHystrix
注解来开启hysterix
:
@SpringBootApplication
// 本服务启动后会自动注册进eureka服务中
@EnableEurekaClient
@EnableFeignClients(basePackages ={"com.chaytech.**"} )
@ComponentScan(basePackages = {"com.chaytech.**"})
@EnableHystrix // 开启hysterix
public class UserConsumerHystrix80_Application {
public static void main(String[] args) {
SpringApplication.run(UserConsumerHystrix80_Application.class,args);
}
}
下面我们来启动eureka集群、用户微服务spring-cloud-provider-user
、消费者服务consumer-hystrix
:
进行测试:
请求:127.0.0.1:8080/consumer/user/getUser/1
可以看到正常返回数据了
请求:127.0.0.1:8080/consumer/user/getUserHystrix/1
返回了系统繁忙,请稍后再试!
,这正是我们定义的服务出现故障后的userHystrixFallback
方法返回的内容。
有些同学可能有些疑问,此时服务是正常的啊,为什么会返回这个呢?我们来看一下前面定义的getUserHystrix
方法:
@GetMapping("/consumer/user/getUserHystrix/{id}")
// 一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
@HystrixCommand(fallbackMethod = "userHystrixFallback")
public UserEntity getUserHystrix(@PathVariable("id") Integer id) {
Logger.info("getUserHystrix thread name is {}", Thread.currentThread().getName());
try{
Thread.sleep(1500);
}catch (Exception e){
}
return userApi.getUser(id);
}
可以看到我在方法内部使当前线程休眠了1.5秒,Hystrix
默认响应时间是1秒,超过1秒将会触发服务降级,这里我们让线程休眠了1.5秒,很显然超过了Hystrix
默认的响应时间,因此Hystrix
进行了降级返回。这里有一点需要注意,我们虽然进行了降级返回,但是方法还是会继续往下执行的,有兴趣的同学可自行测试。
Hystrix
支持禁用响应超时进行服务降级(不建议禁用,可以配置将超时时间配置长一点),配置如下:
hystrix:
command:
default:
execution:
timeout: 5000 # 超时时间设置
enabled: false # 禁用服务超时进行服务降级
下面我们人为来模式服务故障,将spring-cloud-provider-user
服务停掉后,再次访问:
请求:127.0.0.1:8080/consumer/user/getUser/1
可以看到getUser
因为没有配置容错处理,直接返回了500,这种信息是不友好的。
请求:127.0.0.1:8080/consumer/user/getUserHystrix/1
而getUserHystrix
依旧返回了系统繁忙,请稍后再试!
,进行了容错处理。
上面演示的是直接在目标方法上增加@HystrixCommand
注解,进行容错处理。有些同学肯能会想到,那我如果有好多接口都需要进行容错处理,岂不是每个方法上都要加上这个注解,并且还要定义一个降级回掉方法,工作量太大了,有没有其它方法?
答案是:有的,下面我们来介绍另外一种服务容错处理方式。
修改spring-cloud-api
项目,创建一个类UserFallBack
去实现UserApi
,注意UserFallBack
要加上@Component
注解,交给Spring容器管理,否则spring将识别不到:
@Component
public class UserFallBack implements UserApi {
@Override
public boolean createUser(UserEntity user) {
return false;
}
@Override
public UserEntity getUser(Integer id) {
UserEntity userEntity = new UserEntity("系统繁忙,请稍后再试!");
return userEntity;
}
@Override
public List<UserEntity> listUser() {
return null;
}
}
UserFallBack
类里的方法返回的内容,就是服务出现故障后,进行服务降级返回的内容,可以自行定义。
修改UserApi
,将@FeignClient
添加fallback
属性,fallback
指向的就是前面新建的UserFallBack
:
//@FeignClient(value = "USER-PROVIDER")
@FeignClient(value = "USER-PROVIDER", fallback = UserFallBack.class)
public interface UserApi {
@PostMapping("/user/createUser")
public boolean createUser(@RequestBody UserEntity user);
@GetMapping("/user/getUser/{id}")
public UserEntity getUser(@PathVariable("id") Integer id);
@GetMapping("/user/listUser")
public List<UserEntity> listUser();
}
因为feign
已经集成了hystrix
,默认是关闭的,我们只需修改consumer-feign-hystrix
的application.yml
文件,开启hystrix
即可:
# 开启hystrix
feign:
hystrix:
enabled: true
然后将spring-cloud-provider-user
和consumer-feign-hystrix
服务启动,如果consumer-feign-hystrix
启动失败,则需要在启动类上加上@ComponentScan
指定扫描的包:
@SpringBootApplication
// 本服务启动后会自动注册进eureka服务中
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.chaytech.**"})
@ComponentScan(basePackages = {"com.chaytech.**"})
public class UserConsumerFeignHystrix80_Application {
public static void main(String[] args) {
SpringApplication.run(UserConsumerFeignHystrix80_Application.class,args);
}
}
请求:127.0.0.1:8080/consumer/user/getUser/1
可正常访问
将spring-cloud-provider-user
服务停掉,再次请求127.0.0.1:8080/consumer/user/getUser/1
此时因为我们将服务停掉无法通讯,从而进行了服务降级返回我们设置的指定信息。
@FeignClient
除了可以使用fallback
指定服务降级处理,还可以使用fallbackFactory
来指定:
@Component
public class UserFallBackFactory implements FallbackFactory<UserApi> {
@Override
public UserApi create(Throwable throwable) {
return new UserApi() {
@Override
public boolean createUser(UserEntity user) {
return false;
}
@Override
public UserEntity getUser(Integer id) {
UserEntity userEntity = new UserEntity("系统繁忙,请稍后再试!");
return userEntity;
}
@Override
public List<UserEntity> listUser() {
return null;
}
};
}
}
//@FeignClient(value = "USER-PROVIDER")
//@FeignClient(value = "USER-PROVIDER", fallback = UserFallBack.class)
@FeignClient(value = "USER-PROVIDER", fallbackFactory = UserFallBackFactory.class)
public interface UserApi {
@PostMapping("/user/createUser")
public boolean createUser(@RequestBody UserEntity user);
@GetMapping("/user/getUser/{id}")
public UserEntity getUser(@PathVariable("id") Integer id);
@GetMapping("/user/listUser")
public List<UserEntity> listUser();
}
此处就不再演示效果了,可自行测试。
总结
本次我们演示了如何利用hystrix
实现了几种不同的服务降级处理。有些同学可能会有疑问,标题说了熔断和降级啊,全文都在讲降级,熔断哪去了?此处告诉你答案:熔断和降级是配合使用的,hystrix
默认是开启熔断的,@EnableHystrix
注解点进去可以看到@EnableCircuitBreaker
注解,此注解就是用来开启熔断的。熔断后的处理就是我们指定的fallback
或者fallbackFactory
。