前言
分布式系统面临的问题:复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用影响时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。
什么是Hystrix?
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方法返回一个符合预期的、可处理的备选响(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证凉凉服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix重要概念:
1.服务降级(fallback):(一般放在客户端)
服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示。
如:1.程序运行异常
2.超时
3.服务熔断触发服务降级
4.线程池/信号量打满也会导致服务降级
注:只要当前服务不可用了,就做服务降级
2.服务熔断(break):
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
3.服务限流(flowlimit):
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。
1.服务降级
创建一个Maven工程
项目源码https://pan.baidu.com/s/1nf4mKYwZ25pQj9RZOhCfWg 提取码:ccgl
创建一个eureka服务注册具体配置参考https://blog.csdn.net/javaasd/article/details/108791307这里只需要集成一个单机版的即可
创建一个服务提供者cloud-provider-hystrix-payment8008(这里的服务降级使用单个处理)
1.添加依赖
<!-- 引入hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.配置yml文件(源码里面有)
3.启动类上加上注解
@EnableEurekaClient
@EnableCircuitBreaker
4.创建业务实现层PaymentServiceImpl(接口省略)
@Service
public class PaymentServiceImpl implements PaymentService {
@Override
public String paymentInfo_ok(Integer id) {
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_ok: "+id+"哈哈哈";
}
//服务降级
//如果处理不了转别的方法:这是单个的方法
@HystrixCommand(fallbackMethod = "timeOuts",commandProperties = {
//五秒钟以内是正常的业务逻辑
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
})
@Override
public String timeOut(Integer id) {
try {
//睡眠三秒
TimeUnit.MILLISECONDS.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+" timeOut: "+id+"耗时3秒钟";
}
@Override
public String timeOuts(Integer id) {
return "系统超时了让"+id+"大哥出面解决";
}
}
5.创建controller层(直接创建接口调用paymentInfo_ok()和timeOut()方法即可)
接下来创建一个客户消费端cloud-consumer-feign-hystrix-order80(降级群处理版)
1.添加依赖
<!-- 引入hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.配置文件yml(省略部分)
feign:
hystrix:
enabled: true
3.启动类上加上
@EnableFeignClients
@EnableHystrix
4.创建接口PaymentHystrixService(调用服务端controller里面的接口)
@Component
//name=注册器上的服务名称,fallback=如果需要降级调用的降级类
@FeignClient(name = "CLOUD-PAYMENT-HYSTRIX-SERVICE",fallback = PaymentHystrixServiceImpl.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeOut/{id}")
String timeOut(@PathVariable("id") Integer id);
}
5.创建一个降级类PaymentHystrixServiceImpl
@Component
public class PaymentHystrixServiceImpl implements PaymentHystrixService {
//统一降级处理
@Override
public String paymentInfo_ok(Integer id) {
return "paymentInfo_ok--连接失败";
}
@Override
public String timeOut(Integer id) {
return "timeOut--连接失败";
}
}
5.创建controller层OrderHystrixController
@RestController
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_ok(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeOut/{id}")
public String timeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.timeOut(id);
return result;
}
}
服务降级结尾
同时启动cloud-eureka-server7001、cloud-provider-hystrix-payment8008和cloud-consumer-feign-hystrix-order80。
访问cloud-consumer-feign-hystrix-order80的paymentInfo_ok()访问结果如下:
可以看到,这是可以正常访问的。(然后我们把cloud-provider-hystrix-payment8008服务端给关闭,模拟服务崩溃)
在访问的时候就会提示访问失败,而不会返回错误的编码给用户。
到此服务降级结束。
服务熔断
1.根据上面的服务降级的例子稍作修改,这次只修改服务端cloud-provider-hystrix-payment8008(其余二个不做任何改变)
2.修改cloud-provider-hystrix-payment8008中的PaymentServiceImpl(接口省略)
/**
*涉及到断路器的三个重要参数:快照时间窗、请求总数阀值、错误百分比阀值。
*
* 1: @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "10"),
* 快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为
* 最近的10秒。
*
* 2:@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
* 请求总数阀值:在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认20,意味着在10秒内,如果该
* Hystrix命令的调用次数不足20次,即使所有的请求都超时或其它原因失败,断路器都不会打开。
*
* 3:@HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value = "60"),
* 错误百分比阀值:当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15
* 次发生了超时异常,也就是超过50%的错误(百分比)在默认设定为50%阀值情况下,这时候就会将断路器打开。
*/
//==============服务熔断======================
@HystrixCommand(fallbackMethod = "backs",commandProperties = {
@HystrixProperty(name="circuitBreaker.enabled",value = "true"),//是否开启断路器
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//时间窗口期
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value = "60"),//失败率跳闸
})
@Override
public String payment(@PathVariable("id") Integer id){
Integer ids = id;
System.out.println("a");
if(ids < 0){
throw new RuntimeException("id不能小于0");
}else{
String ser = IdUtil.simpleUUID();
System.out.println("b");
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+ser;
}
}
public String backs(@PathVariable("id") Integer id){
System.out.println("c");
return "id,不能小于0";
}
3.新增controller中的方法
//==============服务熔断======================
@GetMapping("/payment/hystrix/pay/{id}")
public String payment(@PathVariable("id") Integer id){
String result = paymentService.payment(id);
return result;
}
启动cloud-eureka-server7001和cloud-provider-hystrix-payment8008即可,我们在payment方法中写了判断,如果id小于0就会产生异常,如果大于等于0就可以正常访问,返回的内容是
如果是错误的会返回
我们疯狂的访问id为负数的接口,然后快速的把id的值改为非负数再次访问,结果如下
发现还是返回错误的结果,这个时候只需要等待几秒钟即可返回正常的结果,这就是服务熔断。
服务熔断原理
实现服务熔断之后,再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback。通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟地效果。
原来地主逻辑要如何恢复呢?
Hystrix为我们实现了自动恢复功能。当断路器打开,对主逻辑进行熔断之后,Hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时地成为主逻辑,当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来地主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
搭建图形化Hystrix Dashboard
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起地请求地执行信息,并统计报表和图形地形式展示给用户,包括每秒执行多少请求,多少成功、多少失败等。Spring Cloud提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
1.在上面的示例上在从新创建一个监控的工程cloud-consumer-hystrix-dashboard9001
2.引入依赖
<!-- 引入hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
3.配置application.yml配置文件(配置一个9001服务端口即可,这里省略)
4.启动类上加上注解
@EnableHystrixDashboard
5.启动项目,然后访问http://localhost:9001/hystrix端口(访问出现下图的内容,即配置成功)
给需要监控的项目配置(cloud-provider-hystrix-payment8008)
1.pom.xml文件添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.启动类上加熔断器注解
@EnableCircuitBreaker
3.启动类中写以下代码
/**
* 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑,
* 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑,
* 只要在自己的项目里配置上下面的servlet就可以了。
* @return
*/
@Bean
public ServletRegistrationBean getServer(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
return registrationBean;
}
然后启动cloud-provider-hystrix-payment8008,并在监控页面配置访问的项目
点击Monitor Stream显示内容如下
访问cloud-provider-hystrix-payment8008项目中的接口,图表也是会发生变化的。到这,搭建图形化Hystrix Dashboard算是完成了。
转载自原文链接, 如需删除请联系管理员。
原文链接:Hystrix服务降级,转载请注明来源!