下一篇 :11. 服务降级 - Hystrix - 解决问题
声明:
- 原作者:csdn:yuan_404
文章目录
- Hystrix官宣,停更进维
1 . 概述
1.1 雪崩效应
分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应。
对于高流量的应用来说,单一的后端服务可能会导致所有服务器上的所有资源都在几秒钟内饱和。更糟的是,这样应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离、管理,以便单一依赖关系的失败,不能取消整个应用程序。
1.2. 什么是Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等, Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
1.3. Hystrix的作用
- 提供保护并控制通过第三方客户端库(通常是通过网络)访问的依赖项的延迟和失败。
- 服务熔断 :停止复杂的分布式系统中的级联故障。
- 快速失败,迅速恢复。
- 服务降级 :回退并在可能的情况下正常降级。
- Hystrix-Dashboard流监控 :启用近乎实时的监视,警报和操作控制。
2 . Hystrix重要概念
2.1 服务降级
- 服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示,fallback
- 哪些情况会触发降级
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池 / 信号量打满也会导致服务降级
2.2 服务熔断
- 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
- 服务的降级->进而熔断->恢复调用链路
2.3 服务限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
3 . Hystrix 环境搭建
3.1 Provider 搭建
-
新建模块 :cloud-provider-payment-Hystrix-8007
-
修改 POM
<dependencies> <!--新增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-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>com.demo.springcloud</groupId> <artifactId>cloud-api-commons</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>
-
编写 YML
server: port: 8007 eureka: client: #表识不向注册中心注册自己 register-with-eureka: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务 fetch-registry: true service-url: #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址 defaultZone: http://localhost:7001/eureka, http://localhost:7002/eureka spring: application: name: cloud-provider-hystrix-payment
-
编写主启动类
@SpringBootApplication @EnableEurekaClient public class PaymentHystrixMain8007 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixMain8007.class,args); } }
-
业务类
-
Service
PaymentService
public interface PaymentService { //成功 public String paymentInfo_OK(Integer id); //失败 public String paymentInfo_TimeOut(Integer id); }
PaymentServiceImpl
@Service public class PaymentServiceImpl implements PaymentService { //成功 @Override public String paymentInfo_OK(Integer id){ return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"哈哈哈" ; } //失败 @Override public String paymentInfo_TimeOut(Integer id){ int timeNumber = 3; try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();} return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber; } }
-
Controller :PaymentController
@RestController @Slf4j public class PaymentController { @Autowired private PaymentService paymentService; @Value("${server.port}") private String serverPort; @GetMapping("/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id){ String result = paymentService.paymentInfo_OK(id); log.info("*******result:"+result); return result; } @GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ String result = paymentService.paymentInfo_TimeOut(id); log.info("*******result:"+result); return result; } }
-
测试
启动 Eureka-7001、provider-Hystrix-8007
访问 http://localhost:8007/payment/hystrix/ok/31
访问 http://localhost:8007/payment/hystrix/timeout/31
访问正常,继续下面的操作
- 上述在非高并发情形下,还能勉强满足
- 下面测试一下高并发的情况
3.2 Jmeter压测测试
- Jmeter 官网下载地址 :https://jmeter.apache.org/download_jmeter.cgi
- 我找的一个教程 :https://www.cnblogs.com/monjeo/p/9330464.html
-
打开 Jmeter
-
新建一个测试计划
-
配置线程组
-
添加 HTTP 请求
-
进行配置
- 保存,启动
-
刷新之前的两条请求
发现都会变慢一点
如果并发量更大,就会更慢,甚至卡死
4 . 加上 Consumer- Hystrix
4.1 环境搭建
-
新建 cloud-consumer-feign-hystrix-order-80
使用 Feign 做负载均衡
-
修改 POM
<dependencies> <!--新增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>com.demo.springcloud</groupId> <artifactId>cloud-api-commons</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>
-
编写 YML
server: port: 80 spring: application: name: cloud-provider-hystrix-order eureka: client: register-with-eureka: true fetchRegistry: true service-url: defaultZone: http://localhost:7001/eureka, http://localhost:7002/eureka
-
编写主启动类
@SpringBootApplication @EnableFeignClients public class OrderHystrixMain80 { public static void main(String[] args) { SpringApplication.run(OrderHystrixMain80.class,args); } }
-
业务类
-
PaymentHystrixService
@Component @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT") public interface PaymentHystrixService { @GetMapping("/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id); @GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id); }
-
OrderHystrixController
@RestController @Slf4j public class OrderHystrixController { @Autowired private PaymentHystrixService paymentHystrixService; @Value("${server.port}") private String serverPort; @GetMapping("/consumer/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_OK(id); log.info("*******result:"+result); return result; } @GetMapping("/consumer/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_TimeOut(id); log.info("*******result:"+result); return result; } }
-
测试
访问 :http://localhost/consumer/payment/hystrix/ok/31
4.2 高并发测试
-
启动 Jmeter
-
访问 Consumer 的服务
发现也开始变得有点慢了。
4.3 原因分析
- 8007 同一层次的其他接口服务被困死
- 因为tomcat线程里面的工作线程已经被挤占完毕
- 这就会导致 8007 访问其他服务时也会变卡
- 80此时调用 8007,客户端访问响应缓慢,转圈圈