目录
一、概述
1.1. 分布式系统面临的问题
雪崩效应
微服务之间进行rpc或者http调用时,我们一般都会设置调用超时,失败重试等机制来确保服务的成功执行,看上去很美,如果不考虑服务的熔断和限流,就是雪崩的源头。
假设我们有两个访问量比较大的服务A和B,这两个服务分别依赖C和D,C和D服务都依赖E服务
A和B不断的调用C,D处理客户请求和返回需要的数据。当E服务不能供服务的时候,C和D的超时和重试机制会被执行
由于新的调用不断的产生,会导致C和D对E服务的调用大量的积压,产生大量的调用等待和重试调用,慢慢会耗尽C和D的资源比如内存或CPU,然后也down掉。
A和B服务会重复C和D的操作,资源耗尽,然后down掉,最终整个服务都不可访问。
常见的导致雪崩的情况有以下几种:
- 程序bug导致服务不可用,或者运行缓慢
- 缓存击穿,导致调用全部访问某服务,导致down掉
- 访问量的突然激增。
- 硬件问题,这感觉只能说是点背了⊙︿⊙。
虽然雪崩效应的产生千万条,保证服务的不挂机,和流畅运行是我们不可推卸的责任,对应雪崩效应还是有很多保护方案的。
怎么避免雪崩呢?
- 服务降级:当服务出现异常,或者宕机,再或者超过了指定的访问时间,直接终止访问线程,立马返回给客户一个响应信息,比如告诉客户端服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示。(12306抢票的时候经常会出现这个情况,就是避免服务器崩掉)
- 服务熔断:类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
- 服务限流:秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
1.2. Hystrix是什么
使用Hystrix可以很轻松的实现上面提到的服务降级、服务熔断、服务限流,而Hystrix也被称之为断路器/熔断器
。
Hystrix是Netfix旗下的一个框架。cloud最火的初期,也就是cloud的Netfix五大神兽,而Hystrix就是其中一个。
Hystrix官网:https://github.com/Netflix/Hystrix
官网也明确提出Hystrix已经进入维护模式,基本上不会出什么新功能了,现在取而代之的是cloud Alibaba 的 Sentinel。当然Sentinel也是基于Hystrix设计理念及其思想而开发的,所以Hystrix我认为还是有必要学习一下的。
二、演示服务雪崩场景
服务雪崩就是服务与服务之间调用出现的问题,那么我们就需要准备一个调用方,和一个提供接口方。
2.1. 提供方接口
@RestController
@Slf4j
public class PaymentController {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = "线程池:" + Thread.currentThread().getName() +
"paymentInfo_OK,id: " + id + "\t" + "O(∩_∩)O";
log.info("****result: " + result);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
try {
// 通过延时三秒,来演示雪崩场景
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
String result = "线程池:" + Thread.currentThread().getName() +
"paymentInfo_TimeOut,id: " + id + "\t" + "O(∩_∩)O,耗费3秒";
log.info("****result: " + result);
return result;
}
}
2.2. 消费方接口
@RestController
@Slf4j
public class PaymentController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String url3 = "http://localhost:8001/payment/hystrix/ok/{1}";
String result2 = restTemplate.getForObject(url3, String.class, id);
log.info(result2);
return result2;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String url3 = "http://localhost:8001/payment/hystrix/timeout/{1}";
String result2 = restTemplate.getForObject(url3, String.class, id);
log.info(result2);
return result2;
}
}
2.3. jmeter压测测试
上述接口只是简单的一个消费方和提供方,主要演示提供方被频繁调用,但是由于接口响应时间较长,而导致服务雪崩。
当我们正常调用肯定是没什么问题的,timeout这个接口调用会耗时3s,一旦3s内出现的并发连接特别多,这时候会导致什么样的场景呢?接下来通过jmeter进行压测。
jmeter官网下载:https://jmeter.apache.org/download_jmeter.cgi
安装完后双击运行即可
汉化:打开bin目录下的jmeter.properties配置文件,然后添加language = zh_cn
即可。
1.右键添加线程组

这里可以设置并发数,来20000个并发压死8001, 20000个请求都去访问paymentlnfo_TimeOut服务

2.然后再右键添加http请求

3.为线程添加监听器

4.启动测试
这时当我开启压测后,然后通过消费方再调用提供方的接口的时候,不管是超时的接口还是正常的接口都会直接一直转圈。为什么会出现这种情况?
tomcat的默认的工作线程数为200,当某一时间大于200没有多余的线程来分解压力和处理,就需要排队处理请求。
而这个接口本身请求就慢,长达3s,会导致一时间产生大量阻塞,并且会引起调用他的服务也会进入阻塞状态。这时候线程全被占了,全被占了意味着服务跟挂了没区别,响应快的接口也会进入阻塞状态。
以上还只是线程产生的影响,假如这个接口占用的堆内存较多,一时间并发很大,而且都进入阻塞状态,堆内存也很容易爆满,堆内存一旦爆满很容易宕机。
正因为有上述故障或不佳表现才有我们的降级/容错/限流等技术诞生
- 调用提供方时候设定超时时间,假如超时了,调用者不能一直卡死等待,必须有服务降级
- 假如提供方宕机了,调用者也不能一直卡死等待,必须有服务降级
- 提供方是OK的,调用者自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级
总结:超时不再等待,出错要有兜底
三、Hystrix服务降级
3.1. 设置降级
设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback。
1.引入Hystrix的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.在容易超时的方法添加注解
这个注解代表的意思就是假如当前方法访问超过1s,就调用当前类的paymentInfo_TimeOutHandler
方法。这个注解可以添加在controller层也可以添加在service的某个方法上。
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
})
@GetMapping("/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
String result = "线程池:" + Thread.currentThread().getName() +
"paymentInfo_TimeOut,id: " + id + "\t" + "O(∩_∩)O,耗费3秒";
log.info("****result: " + result);
return result;
}
public String paymentInfo_TimeOutHandler(Integer id) {
return "/(ㄒoㄒ)/调用支付接口超时或异常:\t" + "\t当前线程池名字" + Thread.currentThread().getName();
}
3.启动类添加@EnableCircuitBreaker
4.访问测试,这时候会发现不管是调用方调用提供方接口,还是单独访问提供方接口都会出现以下情况:
测试的细节一点会发现,当方法超过指定的时间,并没有立马终止线程返回降级方法,还会继续走下面的方法,原因就是我们上面用了try-catch,在try-catch当中sleep 3s,而我们设置的是超过1s触发降级,触发降级说白了就是由hystrix直接将线程抛异常,然后终止执行,也就是他触发降级的时候,刚刚好是在try-catch,被捕捉到了,这样就会导致超时过后,线程仍然会继续执行。然后需要等待执行完,才能返回降级方法的返回值。
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
String result = "线程池:" + Thread.currentThread().getName() +
"paymentInfo_TimeOut,id: " + id + "\t" + "O(∩_∩)O,耗费3秒";
log.info("****result: " + result);
return result;
}
当我们把try-catch去掉之后会发现,一旦1s内没有执行完,立马返回降级方法设置的返回值。正常开发当中我们肯定不允许出现明明降级了线程还继续往下走的场景,所以一定要注意try-catch!
3.2. OpenFeign结合hystrix
假如消费端使用的是OpenFeign来调用提供方接口的,我们如何降级呢?
@FeignClient(name = "paymentService", url = "http://localhost:8001")
public interface PaymentService {
@GetMapping(value = "/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping(value = "/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
1.添加hystrix依赖
2.启动类添加注解@EnableHystrix
3.调用方接口添加注解
@GetMapping("/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result2 = paymentService.paymentInfo_TimeOut(id);
System.out.println("123");
log.info(result2);
return result2;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
4.配置文件添加配置:
感兴趣的可以测试,加了配置,不会输出我打印的123,不加会输出打印123
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000 #设置feign调用超时触发fallback的时间,默认是1s
假如配置文件当中不配置hystrix的timeoutInMilliseconds配置,会出现一种情况@HystrixProperty注解当中配置的超时时间失效,直接超过1s立马返回降级方法结果。感兴趣的可以自己试试。
触发降级之后直接会返回结果,就算有未执行的代码,线程会直接被终止掉
与OpenFeign结合的还有一种方式:
1.创建PaymentFallbackService类
@Component
public class PaymentFallbackService implements PaymentService {
@Override
public String paymentInfo_OK(Integer id) {
return "服务调用失败,提示来自:cloud-consumer-feign-order80";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "服务调用失败,提示来自:cloud-consumer-feign-order80";
}
}
2.FeignClient指定fallback 的类
@FeignClient(name = "paymentService", url = "http://localhost:8001", fallback = PaymentFallbackService.class)
public interface PaymentService {
@GetMapping(value = "/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping(value = "/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
3.添加配置
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 1500 #设置feign调用超时触发fallback的时间,默认是1s
触发降级之后直接会返回结果,就算有未执行的代码,线程会直接被终止掉
4.测试:故意把提供者关掉后,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器
3.3. 代码膨胀问题
每个业务方法对应一个兜底的方法,代码会膨胀,冗余。这时候我们可以设置一个统一的。
@DefaultProperties注解可以统一一个类当中的降级方法。
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class PaymentHystirxController{
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
//加了@DefaultProperties属性注解,并且没有写具体方法名字,就用统一全局的
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
public String payment_Global_FallbackMethod() {
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
}
3.4. 总结
什么时候会触发降级方法?
- 运行异常的时候会触发降级
- 超过我们设定的指定时间,也会降级
- 宕机后也会降级
所谓降级就是直接终止线程,执行降级方法,给客户返回一个好的提示!
如果要使用降级一定要注意 try-catch的问题,避免返回了好的提示,但是线程还继续执行的问题!
四、Hystrix服务熔断
4.1. 断路器是什么
断路器也可以叫做熔断器,起到的作用可以看做是家里用的保险丝。那么什么是保险丝?
保险丝(fuse)也被称为电流保险丝,IEC127标准将它定义为"熔断体(fuse-link)"。其主要是起过载保护作用。电路中正确安置保险丝,保险丝就会在电流异常升高到一定的高度和热度的时候,自身熔断切断电流,保护了电路安全运行。
如下路所示,假如电流过大很容易造成主板烧坏,为了避免这种情况在中间加了一个保护层,就是保险丝,而他的作用就是,当电流达到一定的程度他会自动熔断电流(俗称跳闸),避免造成主板的损坏。
随之而来的又来了个新问题,假如保险丝熔断了,我们怎么恢复呢,现实当中就好比突然跳闸了,我们肯定是需要人工来合上,而程序我们不可能说每次跳闸都人工来启动,这肯定不行,总不能一直有个人来看着吧。
下面来看看微服务大佬Martin Fowler(马丁福乐)
对断路器的解释:https://martinfowler.com/bliki/CircuitBreaker.html
他是将熔断类型分为了三种:
- 熔断打开:请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态
- 熔断关闭:熔断关闭不会对服务进行熔断
- 熔断半开:·部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
熔断打开-》熔断半开-》熔断关闭
熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。
服务熔断和服务降级是紧密相连的,服务降级指的是当调用服务时候,服务产生异常了,或者在指定超时时间范围内没有响应结果,或者宕机了,就立马返回友好提示,而服务熔断是假如五次请求,你三次的都是因为意外而导致没有返回正常结果,而是返回的服务降级的方法或者是直接报异常了,那么我就直接熔断整个服务,熔断之后就算是正常的接口也将是返回友好提示,不会返回正常的结果。这就是两者的区别!
4.2. 熔断实操
//=========服务熔断(以下配置代表的是10秒内,假如请求超过了10次并且失败率大于60%,就直接进入熔断状态)
@GetMapping("/payment/hystrix/circuit/{id}")
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 开启熔断器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 请求次数超过了峰值才有资格熔断
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 时间范围
@HystrixProperty(name = "circuitBreaker.errorpaymentCircuitBreaker_fallbackThresholdPercentage", value = "60"), // 失败率
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
if (id < 0) {
throw new RuntimeException("******id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " + id;
}
http://localhost:8001/payment/hystrix/circuit/1
http://localhost:8001/payment/hystrix/circuit/-1
重点测试: 多次错误之后,就算是正确的访问地址也不能进行,这就代表了已经进入熔断状态。
4.3. 原理(小总结)
涉及到断路器的三个重要参数:快照时间窗、请求总数阀值、错误百分比阀值。
- 快照时间窗: 断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。
- 请求总数阀值: 在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
- 错误百分比阀值: 当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况下,这时候就会将断路器打开。
断路器开启或者关闭的条件
- 当满足一定的阀值的时候(默认10秒内超过20个请求次数)
- 当失败率达到一定的时候(默认10秒内超过50%的请求失败)
到达以上阀值,断路器将会开启,当开启的时候,所有请求都不会进行转发
- 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。
- 再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback。通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
4.4. 熔断的所有配置
@HystrixCommand(fallbackMethod = "str_fallbackMethod",
groupKey = "strGroupCommand",
commandKey = "strCommand",
threadPoolKey = "strThreadPool",
commandProperties = {
// 设置隔离策略,THREAD 表示线程池 SEMAPHORE:信号池隔离
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
// 当隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数)
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 配置命令执行的超时时间
@HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
// 是否启用超时时间
@HystrixProperty(name = "execution.timeout.enabled", value = "true"),
// 执行超时的时候是否中断
@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
// 执行被取消的时候是否中断
@HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
// 允许回调方法执行的最大并发数
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 服务降级是否启用,是否执行回调函数
@HystrixProperty(name = "fallback.enabled", value = "true"),
// 是否启用断路器
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候,
// 如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
// 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过
// circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50,
// 就把断路器设置为 "打开" 状态,否则就设置为 "关闭" 状态。
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
// 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后,
// 会将断路器置为 "半开" 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 "打开" 状态,
// 如果成功就设置为 "关闭" 状态。
@HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
// 断路器强制打开
@HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
// 断路器强制关闭
@HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
// 滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
@HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
// 该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量,断路器在收集指标信息的时候会根据
// 设置的时间窗长度拆分成多个 "桶" 来累计各度量值,每个"桶"记录了一段时间内的采集指标。
// 比如 10 秒内拆分成 10 个"桶"收集这样,所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
// 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。
@HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
// 该属性用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
@HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
// 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。
@HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
// 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数,
// 就从最初的位置开始重写。例如,将该值设置为100, 滚动窗口为10秒,若在10秒内一个 “桶 ”中发生了500次执行,
// 那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。
@HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
// 该属性用来设置采集影响断路器状态的健康快照(请求的成功、 错误百分比)的间隔等待时间。
@HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
// 是否开启请求缓存
@HystrixProperty(name = "requestCache.enabled", value = "true"),
// HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中
@HystrixProperty(name = "requestLog.enabled", value = "true"),
},
threadPoolProperties = {
// 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
@HystrixProperty(name = "coreSize", value = "10"),
// 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,
// 否则将使用 LinkedBlockingQueue 实现的队列。
@HystrixProperty(name = "maxQueueSize", value = "-1"),
// 该参数用来为队列设置拒绝阈值。 通过该参数, 即使队列没有达到最大值也能拒绝请求。
// 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue
// 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
}
)
public String strConsumer() {
return "hello 2020";
}
public String str_fallbackMethod() {
return "*****fall back str_fallbackMethod";
}
五、Hystrix服务限流
Hystrix 可以根据信号量和线程池的大小进行限流降级。
使用Hystrix实现限流的相对来说比较少,关于服务限流,我们后面重点学习阿里巴巴的sentinel
六、服务监控hystrixDashboard
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等
。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
6.1. 搭建hystrixDashboard
hystrixDashboard说白了就是通过自己创建项目,然后进行搭建,这一点非常不友好。
1.添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
server:
port: 9001
2.启动类添加注解@EnableHystrixDashboard
3.需要被hystrixDashboard监控的都需要添加如下依赖
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
4.启动hystrixDashboard,然后访问:http://localhost:9001/hystrix
出现如下页面就代表已经搭建成功了。
6.2. 开始监控服务
需要在被监控的服务启动类下添加如下代码:
/**
*此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
*ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
*只要在自己的项目里配置上下面的servlet就可以了
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
填写监控地址:http://localhost:8001/hystrix.stream
将所有相关/hystrix.stream端点聚合成一个组合/turbine.stream以在 Hystrix Dashboard 中使用的应用程序。
当访问8001的服务后,会发现监控界面会不断的变化:
当8001出现熔断后会出现以下情况:
6.3. 监控界面如何看
- 实心圆: 共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例。
- 曲线: 用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。
七、hystrix工作流程
感兴趣的可以看看!
tips:如果我们没有为命令实现降级逻辑或者在降级处理逻辑中抛出了异常, Hystrix 依然会返回一个 Observable 对象, 但是它不会发射任何结果数据, 而是通过 onError 方法通知命令立即中断请求,并通过onError()方法将引起命令失败的异常发送给调用者。
八、超时配置相关
注意这些在application或者yml配置文件当中是没有代码提示的,但是官网当中提到了如下配置。
不管是使用feign还是使用restTemplate调用,这两个都自动整合了ribbon,所以我们设置超时时间的时候可以指定ribbon的超时时间。
- 如果ribbon和hystrix同时开启都设置了超时时间,则以配置最小的为准。
- 如果关闭了hystrix的超时时间,则请求的超时只依据ribbon中配置的时间。
// 开启ribbon超时管理
ribbon.http.client.enabled=true
// 请求超时时间
ribbon.ReadTimeout=2000
// 连接超时时间
ribbon.ConnectTimeout=2000
// 开启hystrix超时管理
hystrix.command.default.execution.timeout.enabled=true
// hystrix超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
// feign启用hystrix
feign.hystrix.enabled: true