SpringCloud系列文章列表
0. SpringCloud实战专栏介绍准备
1. SpringCloud父工程搭建
2. 服务注册中心之Eureka(单机+集群+Ribbon调用)
3. 服务注册中心之Zookeeper
4. 服务注册中心之Consul
5. eureka、zookeeper和consul三种注册中心之间的区别
6. 负载均衡服务调用之Ribbon
7. 服务调用之OpenFeign
8. Hystrix断路器全面实战总结
9. SpringCloud Gateway网关
10. SpringCloud Config配置中心
11. SpringCloud Bus消息总线
12. SpringCloud Stream消息驱动
13. SpringCloud Sleuth分布式请求链路追踪
0 前言
分布式系统面临的问题?
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败
服务雪崩:
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。
如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,即所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还
可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延
迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
所以通常当你发现一个模块下的某个实例失败之后,这时候这个模块依然还会接收流量,然后这个有问题的模块还被其他的模块调用,这样就会发生级联故障,或者叫做雪崩。
1 Hystrix介绍
官方文档:
https://github.com/Netflix/Hystrix/
目前停止更新进入维护状态
1.1 简介
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,
Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
1.2 功能
下面的案例会围绕着这三种去写
2 Hystrix实战
2.1 搭建项目,制造故障
2.1.1 创建服务提供者 cloud-provider-hystrix-payment8006
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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: 8006
spring:
application:
name: privoider-payment-hystrix8006
eureka:
client:
register-with-eureka: true
fetch-registry: false
service-url:
defaultZone: http://localhost:7001/eureka
instance:
prefer-ip-address: true
instance-id: payment-hystrix8006
- 启动类
@SpringBootApplication
@EnableEurekaClient
- controller
@RestController
public class PaymentController {
Logger logger = LoggerFactory.getLogger(PaymentController.class);
@GetMapping("/payment_ok")
public String payment_ok(){
logger.info("payment_ok : "+Thread.currentThread().getName()+" >>>service is ok");
return "payment_ok : "+Thread.currentThread().getName()+" >>>service is ok";
}
@GetMapping("/payment_timeout")
public String payment_timeout(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
//int age = 10/0;
return "payment_timeout: "+Thread.currentThread().getName()+" >>>service is ok";
}
}
2.1.2 创建服务消费者 cloud-consumer-hystrix-order80
- pom.xml
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</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>
- yml
server:
port: 80
spring:
application:
name: consumer-hystrix-order
eureka:
client:
register-with-eureka: false
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
instance:
prefer-ip-address: true
instance-id: consumer-hystrixOrder
- 启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
- feign的接口
@FeignClient(value = "PRIVOIDER-PAYMENT-HYSTRIX8006")
public interface PaymentClient {
@GetMapping("/payment_ok")
String payment_ok();
@GetMapping("/payment_timeout")
String payment_timeout();
}
- controller
@RestController
@RequestMapping("/consumer")
public class PaymentController {
@Autowired
PaymentClient paymentClient;
@GetMapping("/payment_ok")
public String payment_ok(){
return paymentClient.payment_ok();
}
@GetMapping("/payment_timeout")
public String payment_timeout(){
return paymentClient.payment_timeout();
}
}
2.1.3 测试
- 正常测试
启动cloud-eureka-server7001、cloud-provider-hystrix-payment8006
浏览器访问: http://localhost:8006/payment_ok 很快得到返回值
访问 http://localhost:8006/payment_timeout 等待3s也能正常返回 - 高并发测试
开启Jmeter,来20000个并发压死8006,都访问payment_timeout方法
启动Jmeter后,
再访问http://localhost:8006/payment_ok ,发现也开始转圈圈了;
此时还只是8006自己调用,如果外部的消费端也去调用该服务,那消费者也只能干等,最终导致消费端不满意,服务端8006直接被拖死; - 消费端加入测试
启动cloud-consumer-hystrix-order80,访问http://localhost/consumer/payment_ok,很宽响应结果
开启Jmeter,再次访问,发现也开始转圈圈或者超时报错
2.1.4 故障现象和导致原因
8001同一层次的其他接口服务被困死,因为tomcat工作线程已经被挤占完毕;所以消费端80此时调用8006,访问响应缓慢,转圈圈。
正是因为有上述故障或不佳表现,才有我们的降级、容错、限流等技术诞生。
2.1.5 如何解决?
2.2 服务降级
服务降级一般是在消费端进行降级,本次就演示消费端降级
2.2.1 方法降级配置
修改cloud-consumer-hystrix-order80项目,
- yml1
消费端需要开启熔断器,并配置feign超时时间等
feign:
hystrix:
enabled: true #开启熔断器
client:
config:
default: # feign 的超时设置
connect-timeout: 10000
read-timeout: 10000
hystrix:
command:
default: #default全局有效,service id指定应用有效
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据
enabled: true
isolation:
thread:
timeoutInMilliseconds: 10000 #断路器超时时间,默认1000ms
-
启动类
增加注解 @EnableHystrix -
controller
对payment_timeout方法进行降级配置; //指定降级处理方法payment_timeoutFallBack, 设置2s内没有响应进行降级处理
@GetMapping("/payment_timeout")
@HystrixCommand(fallbackMethod = "payment_timeoutFallBack",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
}) //指定降级处理方法payment_timeoutFallBack, 设置2s内没有响应进行降级处理
public String payment_timeout(){
return paymentClient.payment_timeout();
}
/**新增降级处理方法*/
public String payment_timeoutFallBack(){
return "consumer80:payment_timeoutFallBack: "+Thread.currentThread().getName()+" >>>service is busy";
}
重新启动cloud-consumer-hystrix-order80项目,访问http://localhost/consumer/payment_timeout
如下图,进入到了降级处理方法中;实体项目中会返回一个符合预期的、可处理的备选响应
2.2.2 默认降级配置
思考:当前配置降级,是每一个方法都要配置一个@HystrixCommand。有没有通用的方式能统一配置呢?
答案如下,配置默认的降级方法 defaultFallback
修改controller层:
- controller层增加注解,指定默认降级处理方法defaultFallback
@DefaultProperties(defaultFallback=“defaultFallback”) - 新增方法
error()、error2()以及defaultFallback()
其中error开启降级但不指定方法,error2不配置降级
@GetMapping("/payment_error")
@HystrixCommand
public String error(){//不指定降级方法
int i = 1/0;
return "";
}
@GetMapping("/payment_error2")
public String error2(){//不配置降级
int i = 1/0;
return "";
}
/**默认降级处理方法*/
public String defaultFallback(){
return "consumer80:defaultFallback: "+Thread.currentThread().getName()+" >>>service is busy";
}
重新启动cloud-consumer-hystrix-order80项目,分别访问:
结论:
- 不开启降级,不进行降级 @HystrixCommand
- 降级优先取方法指定的降级方法,未指定走默认降级方法
2.2.3 服务降级配置
思考:openFeign远程调用,同一个服务可能在不同的controller进行调用,如果配置降级的话,就得在所有调用方都得配置重复的降级方法;造成降级和业务逻辑在一起混乱。
解决:对feign的服务接口进行降级,无论谁调用都会走降级;
- 新增对应的降级类,继承自feign的接口
@Component
public class PaymentFallbackService implements PaymentClient {
@Override
public String payment_ok() {
return "PaymentFallbackService:"+Thread.currentThread().getName()+" payment_ok is busy!!!";
}
@Override
public String payment_timeout() {
return "PaymentFallbackService:"+Thread.currentThread().getName()+" payment_timeout is busy!!!";
}
}
- 修改feign的接口类,指定降级处理类
@FeignClient(value = "PRIVOIDER-PAYMENT-HYSTRIX8006",fallback = PaymentFallbackService.class)
- 修改controller,去掉payment_timeout方法的HystrixCommand注解;
启动cloud-consumer-hystrix-order80项目,关闭cloud-provider-hystrix-payment8006项目,模拟服务提供者宕机的异常:
这种配置方式,没有找到给方法单独配置超时时间的地方。可以通过在application.yml中配置feign 的超时设置来统一控制。
2.3 服务熔断
大神文章 : https://martinfowler.com/bliki/CircuitBreaker.html
2.3.1 相关概念
断路器:就是家里的保险丝
在微服务架构中,“断路器模式”的用途也是类似的。当某个服务提供者发生故障(相当于电器发生短路的情况)时,断路器一旦监控到这个情况,会将开关进行自动关闭。之后,在服务消费者调用该故障服务提供者时,直接抛出错误异常,不进行调用,从而避免调用服务的漫长等待。
2.3.2 断路器机制
Hystrix 内置断路器 HystrixCircuitBreaker 实现,一共有三种状态:
- Closed 关闭 (健康)
- Open 打开 (熔断)
- Half Open 半开
1、初始时,断路器处于 Closed状态,链路处于健康状态。当满足如下条件,断路器从 CLOSED 变成 OPEN 状态:
- 周期(可配,HystrixCommandProperties.default_metricsRollingStatisticalWindow = 10000 ms)内,总请求数超过一定量( 可配,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold = 20 ) 。
- 错误请求占总请求数超过一定比例( 可配,HystrixCommandProperties.circuitBreakerErrorThresholdPercentage = 50% ) 。
2、当断路器处于 OPEN 状态,命令执行时,若当前时间超过断路器开启时间一定时间( HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds = 5000 ms ),断路器变成 HALF_OPEN 状态,尝试调用正常逻辑,根据执行是否成功,打开或关闭熔断器
2.3.2 熔断机制概述
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,
当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。
2.3.3 实战
修改服务提供者cloud-provider-hystrix-payment8006
- pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 启动类
增加注解 @EnableCircuitBreaker - controller
//服务熔断
@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.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
})
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = UUID.randomUUID().toString();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
启动项目
访问 http://localhost:8006/payment/circuit/11 ,成功
访问 http://localhost:8006/payment/circuit/-1 ,降级熔断
快速的访问 http://localhost:8006/payment/circuit/-1 10次,然后紧接着访问http://localhost:8006/payment/circuit/11
发现刚开始还是返回id不能负数,重试几次后才恢复正常,这就是断路器在恢复链路的过程。
2.3.4 总结
2.3.4.1 断路器在什么情况下开始起作用?
2.3.4.2 断路器开启或者关闭的条件
2.3.4.3 断路器打开之后
2.3.4.4 ALL配置
2.4 服务限流
后面讲alibaba的Sentinel再说
3 HystrixDashboard服务监控
3.1 概述
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflx通过hystri-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
3.2 实战
3.2.1 代码
- 新建项目cloud-hystrix-dashboard9001
- pom.xml
<!--只需要一个依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- yml
server:
port: 9001
- 启动类
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboard {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboard.class, args);
}
}
- 修改cloud-provider-hystrix-payment8006
启动类中加入以下代码
- 注意:新版本Hystrix需要在主启动类中指定监控路径
@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;
}
3.2.2 演示
启动cloud-eureka-server7001、cloud-provider-hystrix-payment8006、cloud-hystrix-dashboard9001
浏览器访问 http://localhost:9001/hystrix
填写要监控的地址 http://localhost:8006/hystrix.stream ,进入监控页面
快速多次访问接口http://localhost:8006/payment/circuit/11,可以查看到监控页面数据
快速多次访问http://localhost:8006/payment/circuit/-1,可以看到发生了熔断(Circuit处于Open状态)
3.2.3 如何看监控数据
-
7色 分别对应右侧的7中状态
-
1圈
实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,可以在大量的实例中快速的发现故障实例和高压力实例。 -
1线
曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。 -
整图说明
- 整图说明2
点赞+评论+关注
本文源码地址: https://gitee.com/shuaidawang/SpringCloudDemo.git
有错误的地方欢迎指正!可以加入qq交流群: 700637673