SpringCloud第九章,升级篇,服务降级、熔断与实时监控Hystrix
一、Hystrix概述
1、服务雪崩
服务雪崩service avalanche:
假设服务存在如上调用,service a流量波动很大,流量经常会突然性增加!那么在这种情况下,就算Service A能扛得住请求,Service B和Service C未必能扛得住这突发的请求。
此时,如果Service C因为抗不住请求,变得不可用。那么Service B的请求也会阻塞,慢慢耗尽Service B的线程资源,Service B就会变得不可用。紧接着,对Service A的调用就会占用越来越多的资源,进而引起系统崩溃。
如上,一个服务失败,导致整条链路的服务都失败的情形,我们称之为服务雪崩。
服务降级和服务熔断可以视为解决服务雪崩的手段。
2、服务熔断
当下游的服务因为某种原因突然变得不可用或者响应过慢,上游服务为了保证自己服务的可用性,不再继续调用目标服务,直接返回快速释放资源。
如果目标服务好转则恢复调用。
目前流行的熔断器很多,例如阿里出的Sentinel(之后会在博客中介绍),以及最多人使用的Hystrix。
Hystrix配置如下:
##滑动窗口的大小,默认为20
circuitBreaker.requestVolumeThreshold
##过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟
circuitBreaker.sleepWindowInMilliseconds
##错误率,默认50%
circuitBreaker.errorThresholdPercentage
每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。
简单说:
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电。然后调用服务降级的方法并返回友好提示。
3、服务降级
两种场景:
a、当下游服务由于某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放服务器资源,增加响应速度。
b、当下游服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速回馈用户。
简单说:
服务器很忙,请稍后再试,不让客户端等待并立刻返回一个友好提示fallback.
4、服务限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。
5、服务降级和熔断的区别
相同点:
目标一致 都是从可用性和可靠性出发,为了防止系统崩溃;
用户体验类似 最终都让用户体验到的是某些功能暂时不可用;
不同点:
触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
实现方式不太一样,服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断。
二、案例
1、构建cloud-provider-hystrix-payment-8001
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>cloud_2020</artifactId>
<groupId>com.lee.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-hystrix-payment-8001</artifactId>
<dependencies>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.lee.springcloud</groupId>
<artifactId>cloud-api-common</artifactId>
<version>${project.version}</version>
</dependency>
<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>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
主启动类:
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
service
@Service
public class PaymentService {
//正常访问
public String paymentInfo_ok(Integer id){
return "thread:"+Thread.currentThread().getName()+" payment ok id : "+id+" ^_^";
}
//访问超时
public String paymentInfo_timeout(Integer id) throws InterruptedException {
int timeNumber = 3;
TimeUnit.SECONDS.sleep(timeNumber);
return "thread:"+Thread.currentThread().getName()+" payment timeout id : "+id+" ╥﹏╥";
}
}
controller
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String servicePort;
//正常访问
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentService.paymentInfo_ok(id);
return result;
}
//超时访问
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException {
String result = paymentService.paymentInfo_timeout(id);
return result;