SpringCloud学习(四)—— Hystrix熔断器

温故而知新,可以为师矣

本文为拉钩教育java高薪培训班学习笔记与总结

1 微服务中的雪崩效应

什么是微服务中的雪崩效应呢?
微服务中,⼀个请求可能需要多个微服务接⼝才能实现,会形成复杂的调⽤链路。
 
 

2 雪崩效应解决方案

服务熔断

熔断机制是应对雪崩效应的⼀种微服务链路保护机制。我们在各种场景下都会接触到熔断这两个字。⾼压电路中,如果某个地⽅的电压过⾼,熔断器就会熔断,对电路进⾏保护。股票交易中,如果股票指数过⾼,也会采⽤熔断机制,暂停股票的交易。同样,在微服务架构中,熔断机制也是起着类似的作⽤。当扇出链路的某个微服务不可⽤或者响应时间太⻓时,熔断该节点微服务的调⽤,进⾏服务的降级,快速返回错误的响应信息。当检测到该节点微服务调⽤响应正常后,恢复调⽤链路。
注意:
1 )服务熔断重点在 ,切断对下游服务的调⽤
2 )服务熔断和服务降级往往是⼀起使⽤的, Hystrix 就是这样。

服务降级

通俗讲就是整体资源不够⽤了,先将⼀些不关紧的服务停掉(调⽤我的时候,给你返回⼀个预留的值,也叫做 兜底数据),待渡过难关⾼峰过去,再把那些服务打开。
服务降级⼀般是从整体考虑,就是当某个服务熔断之后,服务器将不再被调⽤,此刻客户端可以⾃⼰准备⼀个本地的 fallback回调,返回⼀个缺省值,这样做,虽然服务⽔平下降,但好⽍可⽤,⽐直接挂掉要强。

服务限流

服务降级是当服务出问题或者影响到核⼼流程的性能时,暂时将服务屏蔽掉,待⾼峰或者问题解决后再打开;但是有些场景并不能⽤服务降级来解决,⽐如秒杀业务这样的核⼼功能,这个时候可以结合服务限流来限制这些场景的并发 /请求量
限流措施也很多,⽐如
  • 限制总并发数(⽐如数据库连接池、线程池)
  • 限制瞬时并发数(如nginx限制瞬时并发连接数)
  • 限制时间窗⼝内的平均速率(如GuavaRateLimiternginxlimit_req模块,限制每秒的平均速率)
  • 限制远程接⼝调⽤速率、限制MQ的消费速率等

3 Hystrix简介

[ 来⾃官⽹ ] Hystrix (豪猪 -----> 刺),宣⾔ “defend your app” 是由Netflflix开源的⼀个延迟和容错库,⽤于隔离访问远程系统、服务或者第三⽅库,防⽌级联失败,从⽽提升系统的可⽤性与容错性。 Hystrix 主要通过以下⼏点实现延迟和容错。
  • 包裹请求:使⽤HystrixCommand包裹对依赖的调⽤逻辑。 ⾃动投递微服务⽅法(@HystrixCommand 添加Hystrix控制) ——调⽤简历微服务
  • 跳闸机制:当某服务的错误率超过⼀定的阈值时,Hystrix可以跳闸,停⽌请求该服务⼀段时间。
  • 资源隔离:Hystrix为每个依赖都维护了⼀个⼩型的线程池(舱壁模式)(或者信号量)。如果该线程池已满, 发往该依赖的请求就被⽴即拒绝,⽽不是排队等待,从⽽加速失败判定。
  • 监控:Hystrix可以近乎实时地监控运⾏指标和配置的变化,例如成功、失败、超时、以及被拒绝 的请求等。
  • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执⾏回退逻辑。回退逻辑由开发⼈员 ⾃⾏提供,例如返回⼀个缺省值。
  • ⾃我修复:断路器打开⼀段时间后,会⾃动进⼊半开状态

4 Hystrix熔断应用

⽬的: 简历微服务⻓时间没有响应,服务消费者 —> ⾃动投递微服务 快速失败给⽤户
提示
 
1.  服务消费者⼯程(⾃动投递微服务)中引⼊ Hystrix 依赖坐标(也可以添加在⽗ ⼯程中)
 
<!--熔断器Hystrix-->
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflixhystrix</artifactId>
</dependency>

2.服务消费者⼯程(⾃动投递微服务)的启动类中添加熔断器开启注解 @EnableCircuitBreaker

3. 定义服务降级处理⽅法,并在业务⽅法上使⽤ @HystrixCommand fallbackMethod 属性关联到服务降级处理⽅法
 
/**
 * 提供者模拟处理超时,调⽤⽅法添加Hystrix控制
 * @param userId
 * @return
 */
 // 使⽤@HystrixCommand注解进⾏熔断控制
 @HystrixCommand(
     // 线程池标识,要保持唯⼀,不唯⼀的话就共⽤了
     threadPoolKey = "findResumeOpenStateTimeout",
     // 线程池细节属性配置
     threadPoolProperties = {
         @HystrixProperty(name="coreSize",value ="1"), // 线程数 
         @HystrixProperty(name="maxQueueSize",value="20") // 等待队列⻓度
     },
     // commandProperties熔断的⼀些细节属性配置
     commandProperties = {
     // 每⼀个属性都是⼀个HystrixProperty 
        @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000")
     }
 )
 @GetMapping("/checkStateTimeout/{userId}")
 public Integer findResumeOpenStateTimeout(@PathVariable Long userId) {
 // 使⽤ribbon不需要我们⾃⼰获取服务实例然后选择⼀个那么去访问了(⾃⼰的负载均衡)
     String url = "http://lagou-serviceresume/resume/openstate/" + userId; // 指定服务名
     Integer forObject = restTemplate.getForObject(url,Integer.class);
     return forObject;
 }
 @GetMapping("/checkStateTimeoutFallback/{userId}")
 @HystrixCommand(
     // 线程池标识,要保持唯⼀,不唯⼀的话就共⽤了
     threadPoolKey ="findResumeOpenStateTimeoutFallback",
     // 线程池细节属性配置
     threadPoolProperties = {
         @HystrixProperty(name="coreSize",value ="2"), // 线程数 
        @HystrixProperty(name="maxQueueSize",value="20") // 等待队列⻓度
     },
     // commandProperties熔断的⼀些细节属性配置
     commandProperties = {
     // 每⼀个属性都是⼀个HystrixProperty 
    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000")
     // hystrix⾼级配置,定制⼯作过程细节
     ,
     // 统计时间窗⼝定义
     @HystrixProperty(name ="metrics.rollingStats.timeInMilliseconds",value = "8000"),
     // 统计时间窗⼝内的最⼩请求数
     @HystrixProperty(name ="circuitBreaker.requestVolumeThreshold",value = "2"),
     // 统计时间窗⼝内的错误数量百分⽐阈值
     @HystrixProperty(name ="circuitBreaker.errorThresholdPercentage",value = "50"),
     // ⾃我修复时的活动窗⼝⻓度
     @HystrixProperty(name ="circuitBreaker.sleepWindowInMilliseconds",value = "3000")
     },
     fallbackMethod = "myFallBack" // 回退⽅法
 )
 public Integer
findResumeOpenStateTimeoutFallback(@PathVariable Long userId) {
 // 使⽤ribbon不需要我们⾃⼰获取服务实例然后选择⼀个那么去访问了(⾃⼰的负载均衡)
     String url = "http://lagou-serviceresume/resume/openstate/" + userId; // 指定服务名
     Integer forObject = restTemplate.getForObject(url,Integer.class);
     return forObject;
 }
 /*
 定义回退⽅法,返回预设默认值
 注意:该⽅法形参和返回值与原始⽅法保持⼀致
 */
 public Integer myFallBack(Long userId) {
     return -123333; // 兜底数据
 }
 /**
 * 1)服务提供者处理超时,熔断,返回错误信息
 * 2)有可能服务提供者出现异常直接抛出异常信息
 *
 * 以上信息,都会返回到消费者这⾥,很多时候消费者服务不希望把收到异常/
错误信息再抛到它的上游去
 * ⽤户微服务 — 注册微服务 — 优惠券微服务
 * 1 登记注册
 * 2 分发优惠券(并不是核⼼步骤),这⾥如果调⽤优惠券
微服务返回了异常信息或者是熔断后的错误信息,这些信息如果抛给⽤户很不友好
 * 此时,我们可以返回⼀个兜底数据,预设的默认
值(服务降级)
 *
 *
 */
注意 **
降级(兜底)⽅法必须和被降级⽅法相同的⽅法签名(相同参数列表、相同返回值)
 
4.  可以在类上使⽤ @DefaultProperties 注解统⼀指定整个类中共⽤的降级(兜 底)⽅法
 
 

5 Hystrix舱壁模式(线程池隔离策略)

如果不进⾏任何设置,所有熔断⽅法使⽤⼀个 Hystrix 线程池(10个线程),那么这样的话会导致问题,这个问题并不是扇出链路微服务不可⽤导致的,⽽是我们的线程机制导致的,如果⽅法 A 的请求把 10 个线程都⽤了,⽅法 2请求处理的时候压根都没法去访问 B ,因为没有线程可⽤,并不是 B 服务不可⽤。
 
为了避免问题服务请求过多导致正常服务⽆法访问, Hystrix 不是采⽤增加线程数,⽽是单独的为每⼀个控制⽅法创建⼀个线程池的⽅式,这种模式叫做 舱壁模式 ",也是线程隔离的⼿段。
 
 
 
Hystrix 舱壁模式程序修改
/**
 * 提供者模拟处理超时,调⽤⽅法添加Hystrix控制
 * @param userId
 * @return
 */
 // 使⽤@HystrixCommand注解进⾏熔断控制
 @HystrixCommand(
 // 线程池标识,要保持唯⼀,不唯⼀的话就共⽤了
 threadPoolKey = "findResumeOpenStateTimeout",
 // 线程池细节属性配置
 threadPoolProperties = {
 @HystrixProperty(name="coreSize",value = "1"),
// 线程数
 
@HystrixProperty(name="maxQueueSize",value="20") // 等待队列⻓度
 },
 // commandProperties熔断的⼀些细节属性配置
 commandProperties = {
 // 每⼀个属性都是⼀个HystrixProperty
 
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000")
 }
 )
 @GetMapping("/checkStateTimeout/{userId}")
 public Integer findResumeOpenStateTimeout(@PathVariable Long userId) {
 // 使⽤ribbon不需要我们⾃⼰获取服务实例然后选择⼀个那么去访问了(⾃⼰的负载均衡)
 String url = "http://lagou-serviceresume/resume/openstate/" + userId; // 指定服务名
 Integer forObject = restTemplate.getForObject(url,Integer.class);
 return forObject;
 }
 @GetMapping("/checkStateTimeoutFallback/{userId}")
 @HystrixCommand( 
// 线程池标识,要保持唯⼀,不唯⼀的话就共⽤了
 threadPoolKey = "findResumeOpenStateTimeoutFallback", 
// 线程池细节属性配置
 threadPoolProperties = {
 @HystrixProperty(name="coreSize",value = "2"),// 线程数
 
@HystrixProperty(name="maxQueueSize",value="20") // 等待队列⻓度
 },
 // commandProperties熔断的⼀些细节属性配置
 commandProperties = {
 // 每⼀个属性都是⼀个HystrixProperty
 
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000")
 },
 fallbackMethod = "myFallBack" // 回退⽅法
 )
 public Integer findResumeOpenStateTimeoutFallback(@PathVariable
Long userId) {
 // 使⽤ribbon不需要我们⾃⼰获取服务实例然后选择⼀个那么去访问了(⾃⼰的负载均衡)
 String url = "http://lagou-serviceresume/resume/openstate/" + userId; // 指定服务名
 Integer forObject = restTemplate.getForObject(url,Integer.class);
 return forObject;
 }

6 Hystrix⼯作流程与高级应用

1 )当调⽤出现问题时,开启⼀个时间窗( 10s
2 )在这个时间窗内,统计调⽤次数是否达到最⼩请求数?
如果没有达到,则重置统计信息,回到第 1
如果达到了,则统计失败的请求数占所有请求数的百分⽐,是否达到阈值?
如果达到,则跳闸(不再请求对应服务)
如果没有达到,则重置统计信息,回到第 1
3 )如果跳闸,则会开启⼀个活动窗⼝(默认 5s ),每隔 5s Hystrix会让⼀个请求通过 , 到达那个问题服务,看 是否调⽤成功,如果成功,重置断路器回到第 1步,如果失败,回到第 3
 
/**
 * 8秒钟内,请求次数达到2个,并且失败率在50%以上,就跳闸
 * 跳闸后活动窗⼝设置为3s
 */
 @HystrixCommand(
 commandProperties = {
     @HystrixProperty(name ="metrics.rollingStats.timeInMilliseconds",value = "8000"),
     @HystrixProperty(name ="circuitBreaker.requestVolumeThreshold",value = "2"),
     @HystrixProperty(name ="circuitBreaker.errorThresholdPercentage",value = "50"),
     @HystrixProperty(name ="circuitBreaker.sleepWindowInMilliseconds",value = "3000")
     }
 )
我们上述通过注解进⾏的配置也可以配置在配置⽂件中
# 配置熔断策略:
hystrix:
 command:
  default:
   circuitBreaker:
 # 强制打开熔断器,如果该属性设置为true,强制断路器进⼊打开状态,将会拒绝所有的请求。 默认false关闭的
    forceOpen: false
 # 触发熔断错误⽐例阈值,默认值50%
    errorThresholdPercentage: 50
 # 熔断后休眠时⻓,默认值5秒
    sleepWindowInMilliseconds: 3000 
 # 熔断触发最⼩请求次数,默认值是20
    requestVolumeThreshold: 2 
  execution:
   isolation:
    thread:
 # 熔断超时设置,默认为1秒
     timeoutInMilliseconds: 2000
基于 springboot 的健康检查观察跳闸状态(⾃动投递微服务暴露健康检查细节)
# springboot中暴露健康检查等断点接⼝
management:
 endpoints:
  web:
   exposure:
    include: "*"
 # 暴露健康接⼝的细节
 endpoint:
  health:
   show-details: always

7 Hystrix Dashboard断路监控仪表盘

正常状态是 UP ,跳闸是⼀种状态 CIRCUIT_OPEN ,可以通过 /health查看,前提是⼯程中需要引⼊ SpringBoot actuator(健康监控),它提供了很多监控所需的接⼝,可以对应⽤系统进⾏配置查看、相关功能统计等。
已经统⼀添加在⽗⼯程中
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
如果我们想看到 Hystrix相关数据,⽐如有多少请求、多少成功、多少失败、多少降级等,那么引⼊ SpringBoot 健康监控之后,访问 /actuator/hystrix.stream接⼝可以获取到监控的⽂字信息,但是不直观,所以 Hystrix官⽅还提供了基于图形化的DashBoard (仪表板)监控平 台。 Hystrix仪表板可以显示每个断路器(被@HystrixCommand 注解的⽅法)的状态。
 
1 )新建⼀个监控服务⼯程,导⼊依赖
<!--hystrix-->
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--hystrix 仪表盘-->
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-hystrixdashboard</artifactId>
</dependency> <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eurekaclient</artifactId>
</dependency>
2 )启动类添加 @EnableHystrixDashboard 激活仪表盘
 
package com.lagou.edu;
import org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;
import
org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDa
shboard;
@SpringBootApplication
@EnableHystrixDashboard // 开启hystrix dashboard
public class HystrixDashboardApplication9000 {
 public static void main(String[] args) {
 SpringApplication.run(HystrixDashboardApplication.class, args);
 } }
3) application.yml
server:
 port: 9000
Spring:
 application:
  name: lagou-cloud-hystrix-dashboard
eureka:
 client:
  serviceUrl: # eureka server的路径
  defaultZone:http://lagoucloudeurekaservera:8761/eureka/,http://lagoucloudeurekaserverb:8762/eureka/ 
#把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
 instance:
 #使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
  prefer-ip-address: true
 #⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
  instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
4 )在被监测的微服务中注册监控 servlet(⾃动投递微服务,监控数据就是来⾃于这个微服务)
@Bean
 public ServletRegistrationBean getServlet(){
     HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
     ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
     registrationBean.setLoadOnStartup(1); 
     registrationBean.addUrlMappings("/actuator/hystrix.stream");
     registrationBean.setName("HystrixMetricsStreamServlet");
     return registrationBean;
 }
5 )访问测试 http://localhost:9000/hystrix
 
百分⽐, 10s 内错误请求百分⽐
实⼼圆:
  ⼤⼩:代表请求流量的⼤⼩,流量越⼤球越⼤
  颜⾊:代表请求处理的健康状态,从绿⾊到红⾊递减,绿⾊代表健康,红⾊就代表很不健康
曲线波动图:
  记录了 2 分钟内该⽅法上流量的变化波动图,判断流量上升或者下降的趋势
 

8 Hystrix Turbine聚合监控

之前,我们针对的是⼀个微服务实例的 Hystrix数据查询分析,在微服务架构下,⼀个微服务的实例往往是多个(集群化)
⽐如⾃动投递微服务
实例 1(hystrix) ip1:port1/actuator/hystrix.stream
实例 2(hystrix) ip2:port2/actuator/hystrix.stream
实例 3(hystrix) ip3:port3/actuator/hystrix.stream
按照已有的⽅法,我们就可以结合 dashboard 仪表盘每次输⼊⼀个监控数据流 url,进去查看
⼿⼯操作能否被⾃动功能替代? Hystrix Turbine 聚合(聚合各个实例上的hystrix监控数据)监控 Turbine (涡轮)
思考:微服务架构下,⼀个微服务往往部署多个实例,如果每次只能查看单个实例的监控,就需要经常切换很不⽅便,在这样的场景下,我们可以使⽤ Hystrix Turbine 进⾏聚合监控,它可以把相关微服务的监控数据聚合在⼀起,便于查看。
 

Turbine服务搭建

1 )新建项⽬ lagou-cloud-hystrix-turbine-9001 ,引⼊依赖坐标
<dependencies>
 <!--hystrix turbine聚合监控-->
 <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflixturbine</artifactId>
 </dependency>
 <!--
 引⼊eureka客户端的两个原因
 1、⽼师说过,微服务架构下的服务都尽量注册到服务中⼼去,便于统⼀管理
 2、后续在当前turbine项⽬中我们需要配置turbine聚合的服务,⽐如,我们希望聚合
 lagou-service-autodeliver这个服务的各个实例的hystrix数据流,那随后
 我们就需要在application.yml⽂件中配置这个服务名,那么turbine获取服务下具体实例的数据流的
 时候需要ip和端⼝等实例信息,那么怎么根据服务名称获取到这些信息呢?
 当然可以从eureka服务注册中⼼获取
 -->
 <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eurekaclient</artifactId>
 </dependency>
 </dependencies>
2 )将需要进⾏ Hystrix 监控的多个微服务配置起来,在⼯程 application.yml中开启Turbine 及进⾏相关配置
 
server:
 port: 9001
Spring:
 application:
  name: lagou-cloud-hystrix-turbine
eureka:
 client:
  serviceUrl: # eureka server的路径
   defaultZone:http://lagoucloudeurekaservera:8761/eureka/,http://lagoucloudeurekaserverb:8762/eureka/ 
#把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
 instance:
 #使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
  prefer-ip-address: true
 #⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
  instance-id:${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
#turbine配置
turbine:
 # appCofing配置需要聚合的服务名称,⽐如这⾥聚合⾃动投递微服务的hystrix监控数据
 # 如果要聚合多个微服务的监控数据,那么可以使⽤英⽂逗号拼接,⽐如 a,b,c
 appConfig: lagou-service-autodeliver
 clusterNameExpression: "'default'" # 集群默认名称
3 )在当前项⽬启动类上添加注解 @EnableTurbine ,开启仪表盘以及 Turbine 聚合
 
4 )浏览器访问 Turbine 项⽬, http://localhost:9001/turbine.stream ,就可以看到监控数据了
 
我们通过 dashboard 的⻚⾯查看数据更直观,把刚才的地址输⼊ dashboard 地址栏
 
 
 
 
 
 
 
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值