Hystrix简介
在微服务架构中,各个服务独立部署且服务与服务之前存在相互依赖关系。与单块系统相比,微服务架构中出现服务访问失败的原因和场景非常复杂,需要我们从服务可靠性的角度出发对服务本身以及服务与服务之间的交互过程进行设计。服务可靠性是微服务架构的关键要素之一。
服务可靠性问题同时涉及服务的提供者和消费者 对于服务提供者而言,要做的事情比简单,一旦自身服务发生错误,那么应该快速返回合理的处理结果,也就是要做到快速反馈。而对于服务消费者而言, 事情就比较复杂了,一方面可以采用超时( Timeout)
和重试( Retry 常见方法),另一方面也有一些模式对服务提供者发生失败的场景。
负载均衡是提高系统的性能,而服务熔断是提高服务消费者的容错机制则防止服务故障而导致的系统问题。这些机制包括服务隔离,服务熔断,服务回退,服务限流
- 服务熔断
熔断这一概念来源于电子工程中的熔断器。在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游为了保护系统整体的可用性,可以暂时切断对下游服务的调用,这种牺牲局部,保全对整体的措施叫做熔断。
- 服务降级
服务降级,就是当某个服务熔断后,服务器将不再被调用,此时客户端可以自己准备一个本地的Fallback回调,返回一些数据。
- 服务限流
服务限流就是限制系统的输入和输出的流量达到保护系统的目的。一般来说系统的吞吐量是可以被预测的,为了保护系统的稳定运行,一旦达到需要限流的阙值,就需要限流量并采用少量的措施完成限制流量的目的。
- 服务隔离
服务隔离是将系统按照一定原则划分为若干模块,各个模块之间相对对立,无强依赖。当有故障发生时,能将问题隔离在某个模块内部,不扩散风险不影响整体的系统服务。
- 服务回退
服务回退是在处理服务依赖而导致的异常时也是一种有效的容错机制。当远程调用发生异常时,服务回退不是直接抛出异常,而是产生另外的机制来应对异常,相当于执行了另一条路上的代码而返回的处理结果。
Hystrix组件对Web Service的支持
对RestTemplate的支持
- 引入hystrix的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
@EnableHystrix
启动类激活Hystrix
@EnableCircuitBreaker
在3.0.1版本中已经移除了,2点多版本显示过时。改用@EnableHystrix
实现了前者全部功能。
@SpringBootApplication
@EnableDiscoveryClient
//hystrix熔断处理
//@EnableCircuitBreaker 已过时
@EnableHystrix
public class ProviderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderServiceApplication.class, args);
}
}
- 配置熔断触发的降级逻辑
/**
* 服务降级策略,要求参数和返回值一致
*/
BillMessage default_billMessage(Integer id){
BillMessage billMessage = new BillMessage();
billMessage.setStatus(500);
billMessage.setMessage("服务器繁忙,请稍后再试!");
billMessage.setBill(null);
return billMessage;
}
//降级处理当服务未响应时配置本地的返回数据
@HystrixCommand
注解声明接口保护
//声明式熔断保护,参数配置降级策略
@HystrixCommand(fallbackMethod = "default_billMessage")
@GetMapping(value = "/hystrix/{id}")
BillMessage method1(@Autowired RestTemplate restTemplate, @PathVariable("id") Integer id){
BillMessage forObject = restTemplate.getForObject("http://localhost:8081/bill/" + id, BillMessage.class);
return forObject;
}
各个模块完整代码
controller
@RestController
@RequestMapping(value = "/provider")
public class ProviderController {
@Autowired
ProviderService providerService;
@Autowired RestTemplate restTemplate;
@GetMapping(value = "/{id}")
ProviderMessage getById(@PathVariable int id){
ProviderMessage byId = providerService.getById(id);
return byId;
}
//声明式熔断保护,参数配置降级策略
@HystrixCommand(fallbackMethod = "default_billMessage")
@GetMapping(value = "/hystrix/{id}")
BillMessage method1(@PathVariable("id") Integer id){
BillMessage forObject = restTemplate.getForObject("http://localhost:8081/bill/" + id, BillMessage.class);
return forObject;
}
/**
* 服务降级策略,要求参数和返回值一致
*/
BillMessage default_billMessage(Integer id){
BillMessage billMessage = new BillMessage();
billMessage.setStatus(500);
billMessage.setMessage("服务器繁忙,请稍后再试!");
billMessage.setBill(null);
return billMessage;
}
}
feignclient
@FeignClient(name = "bill-service")
public interface BillClient {
@GetMapping(value = "/bill/{id}")
BillMessage method1(@PathVariable("id") Integer id);
}
启动服务:
服务之间的调用关系
启动熔断处理后访问接口:
关闭BillService服务模拟服务未响应的错误,再次访问接口:
返回本地数据数据熔断处理及服务降级生效。
如果所有接口的返回值类型是一样的就可以配置公共的降级处理方法@DefaultProperties
参数配置为某个方法时,无需在单个接口处再次配置方法。
对Feign的支持
- feign组件的配置及启动
Spring Cloud Netflix Feign配置配置完成后访问接口:
/**
* feign代理api接口
*
*/
@Autowired private BillClient billClient;
@GetMapping(value = "/feign/{id}")
BillMessage method2(@PathVariable("id") Integer id){
BillMessage billMessage = billClient.method1(id);
return billMessage;
}
先配置feign的代理,不懂得可以看我之前的文章。成功访问。
- 配置hystrix组件
- 导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
- 配置文件开启feign对hystrix的支持
feign.hystrix.enabled=true
- feign代理的实现类作为降级方法
@FeignClient(name = "bill-service", fallback = BillClientImpl.class)
public interface BillClient {
@GetMapping(value = "/bill/{id}")
BillMessage method1(@PathVariable("id") Integer id);
}
public class BillClientImpl implements BillClient {
@Override
public BillMessage method1(Integer id) {
BillMessage billMessage = new BillMessage();
billMessage.setBill(null);
billMessage.setMessage("feign配置hystrix返回服务器繁忙!");
billMessage.setStatus(500);
return billMessage;
}
}
- 启动类配置hystrix的启动注解
@EnableHystrix
@SpringBootApplication
@EnableDiscoveryClient
//hystrix熔断处理
//@EnableCircuitBreaker 已过时
@EnableHystrix
//feign的配置
@EnableFeignClients
public class ProviderServiceApplication {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ProviderServiceApplication.class, args);
}
}
启动服务
访问消费者接口
停掉hystrix管理的服务模拟服务繁忙
启动后通过访问hystrix管理的feign接口发现返回500的错误,查了原因后发现是hystrix未生效
解决配置了feign.hystrix.enabled:=true不生效的原因
原因是:feign.hystrix.enabled=true
是springcloud2020以前的版本,而之后的版本是:feign.circuitbreaker.enabled=true
修改配置后重启服务,访问hystrix管理的接口:
hystrix服务熔断及降级处理的方式,使用feign和RestTemplate的区别主要在服务降级:
//声明式熔断保护,参数配置降级策略
@HystrixCommand(fallbackMethod = "default_billMessage")
@GetMapping(value = "/hystrix/{id}")
BillMessage method1(@PathVariable("id") Integer id){
BillMessage forObject = restTemplate.getForObject("http://localhost:8081/bill/" + id, BillMessage.class);
return forObject;
}
/**
* 服务降级策略,要求参数和返回值一致
*/
BillMessage default_billMessage(Integer id){
BillMessage billMessage = new BillMessage();
billMessage.setStatus(500);
billMessage.setMessage("服务器繁忙,请稍后再试!");
billMessage.setBill(null);
return billMessage;
}
RestTemplate服务降级在
controller
层定义降级方法,并通过熔断保护注解的fallbackMethod
参数配置
feign代理的是通过feign代理接口的接口实现类作为降级方法:
@FeignClient(name = "bill-service", fallback = BillClientImpl.class)
public interface BillClient {
@GetMapping(value = "/bill/{id}")
BillMessage method1(@PathVariable("id") Integer id);
}
@Component
public class BillClientImpl implements BillClient {
@Override
public BillMessage method1(Integer id) {
BillMessage billMessage = new BillMessage();
billMessage.setBill(null);
billMessage.setMessage("feign配置hystrix返回服务器繁忙!");
billMessage.setStatus(500);
return billMessage;
}
}
接口实现类要注入到spring 容器中,并在父接口中通过
fallback
参数声明降级方法类
两种实现方法的完整代码如下,包括controller层可feign代理接口:
controller
@RestController
@RequestMapping(value = "/provider")
public class ProviderController {
@Autowired
ProviderService providerService;
@Autowired RestTemplate restTemplate;
@GetMapping(value = "/{id}")
ProviderMessage getById(@PathVariable int id){
ProviderMessage byId = providerService.getById(id);
return byId;
}
//声明式熔断保护,参数配置降级策略
@HystrixCommand(fallbackMethod = "default_billMessage")
@GetMapping(value = "/hystrix/{id}")
BillMessage method1(@PathVariable("id") Integer id){
BillMessage forObject = restTemplate.getForObject("http://localhost:8081/bill/" + id, BillMessage.class);
return forObject;
}
/**
* 服务降级策略,要求参数和返回值一致
*/
BillMessage default_billMessage(Integer id){
BillMessage billMessage = new BillMessage();
billMessage.setStatus(500);
billMessage.setMessage("服务器繁忙,请稍后再试!");
billMessage.setBill(null);
return billMessage;
}
/**
* feign代理api接口
*
*/
@Resource
private BillClient billClient;
@HystrixCommand
@GetMapping(value = "/feign/{id}")
BillMessage method2(@PathVariable("id") Integer id){
BillMessage billMessage = billClient.method1(id);
return billMessage;
}
}
feignclient
@FeignClient(name = "bill-service", fallback = BillClientImpl.class)
public interface BillClient {
@GetMapping(value = "/bill/{id}")
BillMessage method1(@PathVariable("id") Integer id);
}
feignclientimpl
@Component
public class BillClientImpl implements BillClient {
@Override
public BillMessage method1(Integer id) {
BillMessage billMessage = new BillMessage();
billMessage.setBill(null);
billMessage.setMessage("feign配置hystrix返回服务器繁忙!");
billMessage.setStatus(500);
return billMessage;
}
}
总结
使用feign作为服务调用端对hystrix的整合分四个步骤:
- 引入hystrix依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
- feign开启对hystrix的支持
feign.circuitbreaker.enabled=true
- 定义fein代理接口的实现类作为服务降级触发的降级逻辑,并在接口注解使用
fallback
声明该类
//接口
@FeignClient(name = "bill-service", fallback = BillClientImpl.class)
//实现类
/*
实现类要注入到spring容器中
*/
@Component
public class BillClientImpl implements BillClient {
@Override
public BillMessage method1(Integer id) {
BillMessage billMessage = new BillMessage();
billMessage.setBill(null);
billMessage.setMessage("feign配置hystrix返回服务器繁忙!");
billMessage.setStatus(500);
return billMessage;
}
}
- 接口开启hystrix熔断保护
@HystrixCommand
@HystrixCommand
@GetMapping(value = "/feign/{id}")
BillMessage method2(@PathVariable("id") Integer id){
BillMessage billMessage = billClient.method1(id);
return billMessage;
}
Hystrix监控平台
除了实现容错功能外,Hystrix还提供了近乎实时的监控HystrixCommand和HystrixObservableCommand在执行时会生成执行结果和运行指标。这些状态会暴露在Actuator提供的health端点中。只需为项目添加spring-boot-actuator
依赖重启项目即可,访问地址http://localhost:9000/actuator/hystrix.stream
可看到监控数据(端口是启用hystrix熔断保护的端口)。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
发现未找到hystrix服务,查看actuator暴露的接口:
需要通过配置暴露接口:
management.endpoints.web.exposure.include=*
重启项目,会一直ping回去hystrix熔断保护信息:
这时只要访问一个由hystrix熔断保护的接口,监控系统就会返回该接口的监控信息:
可以看到上面文本式的监控信息很不直观,spring cloud提供了DashBoard的图像化监控。Hystrix仪表盘可以显示每个熔断保护的接口及注解了@HystrixCommand
的接口。
要使用hystrix仪表盘还需要再导入一个依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
再启动类上添加@EnableHystrixDashboard
重启服务,输入http://localhost:8082/hystrix
端口是开启熔断保护的端口。
仪表盘有很多功能,如果要实现对hystrix熔断保护的实时监控,需要输入url地址即http://localhost:9000/actuator/hystrix.stream
,点击Monitor Stream
就会跳转到熔断保护的接口信息:
该接口服务在未访问的情况下是没有任何信息的,访问一下熔断保护的接口:
相关信息含义
对于上面的dashborad可以监控某个端口下的服务熔断,对于其他接口要实现监控就需要不断切换,这显然是很麻烦的,Hystrix也提供了Turbine方法帮助全局监控整个系统。
使用turbine也很简单分两步完成。
- 导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
- 配置turbine
# turbine
# 如果监控多个微服务用逗号隔开
turbine.app-config=provider-service
turbine.cluster-name-expression=default
turbine会自动从注册中心自动获取需要监控的微服务,并聚合所有的微服务到/hystrix.stream
接口。
Hystrix基本原理
服务隔离
Hystrix使用命令模式的实现类 HystrixCommand 包装依赖调用逻辑,将每个类型的业务类型请求封装成对应的命令,每个命令在单独线程中执行 创建好的线程池被放入到
ConcurrentHashMap 中,当第二次查询请求过来时,可以直接从 Map中获取该线程池。
HystrixCommand 在执行过程中,执行业务代码的线程与请求线程(比如 Tomcat 线程)分离,请求线程可以自由控制离开的时间,这也就是通常所说的异步编程, Hystrix 是结合RxJava 来实现的异步编程,内部大量使用了 RxJava。RxJava 是一个响应式编程框架 ,可以参考其官方网站做进一步了解 Hystrix 通过设置线程池大小来控制并发访问 ,当线程饱和时可以拒绝服务,防止依赖问题扩散。Hystrix 使用线程池存储当前请求以及对请求做出处理 通过设置任务处理超时时间,并将堆积的请求放入线程池队列,可以应对突发流量。 当流量洪峰来临时,处理不完的请求可将数据存储到线程池中慢慢处理,当使用信号 Hystrix 使用原子计数器来记录当前运行线程数,新的请求到来时先判断计数器的数值,若超过设 定的最大线程数则丢弃该类型的新请求,若不超过则执行计数器+ l,请求返回时执行计数器一 1。信 号量隔离无法应对突发流量。
隔离策略的修改配置
服务熔断
结合服务隔离与服务熔断Hystrix的运行流程:
hystrix配置项
部分图和内容取自微服务架构实战|郑天民 著
服务熔断Hystrix的替换方案Sentinel。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/156246.html