Hystrix介绍
当微服务之间调用的时候,假设A服务调用B服务,B服务调用C服务,如果调用链路上的任何一环出现异常响应时间过长或者服务不可用,就会造成调用方长时间得不到响应或者错误异常响应而占用线程,占用系统资源过多甚至会引起服务雪崩。
Hystrix是一个用于分布式系统的延迟和容错的开源库。在分布式系统里,许多依赖不可避免的调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。
话不多说,开干!!!
-
创建hystrix-service模块,模块的创建过程参考Spring Cloud Eureka服务注册中心
添加依赖有:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--这个依赖是我之后介绍使用WebClient代替RestTemplate需要引入的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
添加application.yml配置
server:
port: 8401 #端口号
spring:
application:
name: hystrix-service #服务名称
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8001/eureka/ #注册中心地址
#这是我声明的一个值,在springboot中通过@Value注解可调用
service-url:
emp-service: http://emp-service # emp-service的调用路径,cloud的模块之间的调用要用服务名称
3.需要通过启动类加上@EnableCircuitBreaker启用对断路器的支持
/**
* @EnableCircuitBreaker 开启断路器功能
*/
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class HystrixServiceApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixServiceApplication.class, args);
}
}
4.参考上一篇文章给RestTemplate和WebClient负载均衡的能力,也就是加入RibbonConfig类
@Configuration
public class RibbonConfig {
/**
* @LoadBalanced 使 WebClient能够进行负载均衡
* @return
*/
@Bean
@LoadBalanced
public WebClient.Builder webClient() {
return WebClient.builder();
}
/**
* @LoadBalanced 使 RestTemplate能够进行负载均衡
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
5.通过本模块调用emp-service的服务来讲解断路器的功能,添加HystrixController来调用emp-service的服务。首先是通过RestTemplate完成调用。
@RestController
@RequestMapping("/emp")
public class HystrixController {
/**
* 注入RestTemplate
*/
@Autowired
private RestTemplate restTemplate;
/**
* 获取application.yml中声明的emp-service的服务调用路径
*/
@Value("${service-url.emp-service}")
private String empServiceUrl;
/**
* @HystrixCommand 可以声明该方法需要使用断路器保护
* fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法
* 注意这里的fallback必须和当前方法拥有相同的返回值,参数
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "fallback")
@GetMapping("/{id}")
public String getEmpById(@PathVariable Long id){
return restTemplate.getForObject(empServiceUrl + "/emp/{1}", String.class, id);
}
public String fallback(@PathVariable Long id) {
return "服务降级处理";
}
}
@HystrixCommand 可以声明该方法需要使用断路器保护,然后fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法
6.启动这四个服务,然后访问http://localhost:8401/emp/11
image查询员工返回200调用成功,刷新几次发现交替在8201和8202端口调用emp-service的根据id查询员工的服务
image负载均衡的调用emp-service成功
image image7.下面停掉8202端口的emp-service的服务,再次刷新
image刷新发现会出现200和服务降级处理两种结果,这是因为当负载均衡到8202端口的时候由于服务已经停掉,所以就开启了断路器,执行服务熔断降级,根据配置的降级处理调用fallback方法,所以返回服务降级处理这个结果。
image8.刚才测试了直接关闭服务之后的降级处理,现在测试一下服务开启的状态,emp-service服务的代码中加一个运行时异常之后的处理结果。
image重新启动emp-service服务,访问localhost:8401/emp/11
返回结果,服务降级处理
image查看一下emp-service服务的控制台,发现报异常了,所以是出现异常然后断路器做了降级处理了。
image9.restTemplate调用时通过@HystrixCommand(fallbackMethod = "fallback")设置服务降级处理,那如果通过WebClient调用呢?直接上代码!!!
@RestController
@RequestMapping("/emp")
public class HystrixController {
/**
* 注入webClient
*/
@Autowired
private WebClient.Builder webClient;
/**
* 注入RestTemplate
*/
@Autowired
private RestTemplate restTemplate;
/**
* 获取application.yml中声明的emp-service的服务调用路径
*/
@Value("${service-url.emp-service}")
private String empServiceUrl;
/**
* @HystrixCommand 可以声明该方法需要使用断路器保护
* fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法
* 注意这里的fallback必须和当前方法拥有相同的返回值,参数
* @param id
* @return
*/
//@HystrixCommand(fallbackMethod = "fallback")
@GetMapping("/{id}")
public Mono<String> getEmpById(@PathVariable Long id){
Mono<String> call = webClient.build()
.get()
.uri(empServiceUrl + "/emp/{id}", id)
.retrieve()
.bodyToMono(String.class);
Mono<String> mono = HystrixCommands
.from(call)
.fallback(Mono.just("服务降级处理"))
.commandName("getEmpById")
.toMono();
return mono;
//return restTemplate.getForObject(empServiceUrl + "/emp/{1}", String.class, id);
}
/*public String fallback(@PathVariable Long id) {
return "服务降级处理";
}*/
}
额。可以发现不需要注解@HystrixCommand,具体写法就是如上面代码,我也是找了好多资料才在https://stackoverflow.com上找到的。
@HystrixCommand won't really work, because Hystrix doesn't threat Mono/Flux any different from Java primitives.
Hystrix doesn't monitor content of Mono, but only the result of call public Mono<WeatherApiResponse> getWeatherByCityName(String cityName).
This result is always OK, because reactive-call-chain creation will always succeed.
What you need, is to make Hystrix threat Mono/Flux differently. In Spring Cloud, there is a builder, to wrap Mono/Flux with HystrixCommand.
Mono<WeatherApiResponse> call = this.getWeatherByCityName(String cityName);
Mono<WeatherApiResponse> callWrappedWithHystrix = HystrixCommands
.from(call)
.fallback(Mono.just(WeatherApiResponse.EMPTY))
.commandName("getWeatherByCityName")
.toMono();
网友评论