• springcloud-Hystrix断路器02(六)


    服务降级

    1. 主要用到@HystrixCommand注解.
    • 先从cloud-provider-hystrix-payment8001自身找问题
    1. 设置自身调用超时时间的峰值, 超过了需要有兜底的方法处理, 作服务降级fallback.
    2. 修改8001部分代码
      • 主启动类激活: @EnableCircuitBreaker
        @EnableCircuitBreaker
        @EnableEurekaClient
        @SpringBootApplication
        public class PaymentHystrixMain8001 {
        
            public static void main(String[] args) {
                SpringApplication.run(PaymentHystrixMain8001.class, args);
            }
        }
      • service业务类
        • 一旦调用服务方法失败并抛出了错误信息后, 会自动调用@HystrixCommand标注好的fallback方法
          @Service
          public class PaymentServiceImpl implements PaymentService {
          
              @Override
              public String paymentInfo_OK(Integer id) {
                  return "线程池: " + Thread.currentThread().getName() + " paymentInfo_OK, id: " + id;
              }
          
              //fallbackMethod属性中填写兜底方法
              @HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler", commandProperties = {
                      //3秒内完成即为正常逻辑
                      @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
              })
              @Override
              public String paymentInfo_Timeout(Integer id) {
          
                  //这里测试, 超时会调用服务降级, 出错也会
                  int timeNumber = 1;
                  int age = 10 / 0;
                  try{
                      TimeUnit.SECONDS.sleep(timeNumber);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
          
                  return "线程池: " + Thread.currentThread().getName() + " paymentInfo_Timeout, id: " + id + ", 耗时: " + timeNumber;
              }
          
          
              public String paymentInfo_TimeoutHandler(Integer id) {
          
                  return "线程池: " + Thread.currentThread().getName() + " 系统繁忙, 请稍后再试, id: " + id + "/(ㄒoㄒ)/~~";
              }
          }
    • 在消费者cloud-consumer-feign-hystrix-order80这里作服务降级.
    1. 其实一般服务降级都是在客户端做的, 这样可以更好的保护自己.
    2. yml中要开启Hystrix
      feign:
        hystrix:
          enabled: true
    3. 主启动类: @EnableHystrix
      @EnableHystrix
      @SpringBootApplication
      @EnableFeignClients
      public class OrderHystrixMain80 {
          public static void main(String[] args) {
              SpringApplication.run(OrderHystrixMain80.class,args);
          }
      }
    4. 业务类
      • controller
        @RestController
        @Slf4j
        public class OrderHystrixController {
        
            @Resource
            private PaymentHystrixService paymentHystrixService;
        
            @GetMapping("/consumer/payment/hystrix/ok/{id}")
            public String paymentInfo_OK(@PathVariable("id") Integer id) {
        
                String result = paymentHystrixService.paymentInfo_OK(id);
                return result;
            }
        
            @HystrixCommand(fallbackMethod = "paymentTimeoutFallbackMethod", commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
            })
            @GetMapping("/consumer/payment/hystrix/timeout/{id}")
            public String paymentInfo_Timeout(@PathVariable("id") Integer id) {
                String result = paymentHystrixService.paymentInfo_Timeout(id);
                return result;
            }
        
            public String paymentTimeoutFallbackMethod(@PathVariable("id") Integer id) {
                return "我是消费者80, 对方支付系统繁忙, 请10秒后重试 /(ㄒoㄒ)/~~";
            }
        }
    • 解决膨胀与耦合
    1. 目前出现的问题
      • 兜底的代码跟业务逻辑混到一起了, 代码耦合度低.
      • 每个业务方法对应一个兜底的方法, 代码太膨胀
    2. 解决代码膨胀
      • 用到注解@DefaultProperties
      • 在具体的方法中, 如果只标有@HystrixCommand, 则使用defaultFallback
      • 但若在@HystrixCommand中指明了fallbackMethod, 则使用自己指明的.
      • 这样通用的和独享的各自分开, 避免了代码膨胀, 合理减少了代码量.
        @RestController
        @Slf4j
        @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod", commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
        })
        public class OrderHystrixController {
        
            @Resource
            private PaymentHystrixService paymentHystrixService;
        
            //什么也不注明则不使用服务降级
            @GetMapping("/consumer/payment/hystrix/ok/{id}")
            public String paymentInfo_OK(@PathVariable("id") Integer id) {
        
                String result = paymentHystrixService.paymentInfo_OK(id);
                return result;
            }
        
            @HystrixCommand
            @GetMapping("/consumer/payment/hystrix/timeout/{id}")
            public String paymentInfo_Timeout(@PathVariable("id") Integer id) {
                String result = paymentHystrixService.paymentInfo_Timeout(id);
                return result;
            }
        
            public String payment_Global_FallbackMethod() {
        
                //下面是全局fallback方法
                return "Global异常处理信息,请稍后再试,(┬_┬)";
            }
        }
    3. 解决代码耦合
      • 只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦合
      • 本例在客户端80完成服务降级处理.
      • 首先写一个类继承PaymentHystrixService, 在其中填写各个功能的降级处理方法.
        @Component
        public class PaymentFallbackService implements PaymentHystrixService {
            @Override
            public String paymentInfo_OK(Integer id) {
                return "---PaymentFallbackService fall back paymentInfo_OK";
            }
        
            @Override
            public String paymentInfo_Timeout(Integer id) {
                return "------PaymentFallbackService paymentInfo_Timeout";
            }
        }
      • 然后在PaymentHystrixService的@FeignClient的fallback属性中填上该实现类
        @Component
        @FeignClient(value="cloud-provider-hystrix-payment", fallback = PaymentFallbackService.class )
        public interface PaymentHystrixService {
        
            @GetMapping("/payment/hystrix/ok/{id}")
            String paymentInfo_OK(@PathVariable("id") Integer id);
        
            @GetMapping("/payment/hystrix/timeout/{id}")
            String paymentInfo_Timeout(@PathVariable("id") Integer id);
        }
      • 这样就实现了解耦合, 
    4. 注: 我们面对的异常通常有
      • 运行时异常
      • 超时
      • 宕机

     服务熔断

    • 熔断机制概述
    1. 熔断机制是应对雪崩效应的一种微服务链路保护机制. 当扇出链路的某个微服务出错不可用或响应时间太长时, 会进行服务的降级, 进而熔断该节点微服务的调用, 快速返回错误的响应信息.
    2. 当检测到该节点微服务调用响应正常后, 会恢复调用链路.
    3. Hystrix实现熔断机制
      • Hystrix会监控微服务间调用的状况, 当失败的调用到一定阙值, 缺省是5秒内20次调用失败, 就会启动熔断机制.
      • 熔断机制的注解也是@HystrixCommand
    • 实操
    1. 修改支付微服务: cloud-provider-hystrix-payment8001
      • service方法
            //服务熔断
            //服务熔断
            @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"), //失败率达到多少后跳闸
            })
            public String paymentCircuitBreaker(@PathVariable("id") Integer id){
                if (id < 0){
                    throw new RuntimeException("*****id 不能负数");
                }
                String serialNumber = IdUtil.simpleUUID(); // UUID.randomUUID().toString()
        
                return Thread.currentThread().getName()+"	"+"调用成功,流水号:"+serialNumber;
            }
        
            public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
                return "id 不能负数,请稍候再试,(┬_┬)/~~     id: " +id;
            }
      • 在其对应的controller方法中PaymentController添加
            //---服务熔断
            @GetMapping("/payment/circuit/{id}")
            public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
                String result = paymentService.paymentCircuitBreaker(id);
                log.info("****result" + result);
                return result;
            }
    2. 测试
      • 正确的: http://localhost:8001/payment/circuit/11
      • 错误的: http://localhost:8001/payment/circuit/-11
      • 我们多次访问错误的, 之后访问正确的, 发现刚开始也不能显示正确页面, 再访问几次正确的后, 正确率高了, 才慢慢可以显示正确页面.
      • 这就是 服务的熔断 -> 服务降级 -> 恢复调用链路
    • 服务熔断总结
    1. 熔断类型
      • 熔断打开: 请求不再进行调用当前服务, 内部设置时钟一般为MTTR(平均故障处理时间), 当打开时长达到所设时钟则进入熔断状态
      • 熔断关闭
      • 熔断半开: 部分请求根据规则调用当前服务, 如果请求成功且符合规则则认为当前服务恢复正常, 关闭熔断.
    2. 断路器在什么情况下开始起作用
      • 快照时间窗口: 断路器确定是否打开需要统计一些请求和错误数据, 而统计的时间范围就是快照时间窗, 默认10秒
      • 请求总数阈值: 在快照时间窗内,, 必须满足请求总数阈值才有资格熔断. 默认为20
        • 意味着在10秒内, 如果该hystrix命令的调用次数不足20次, 即使所有的请求都超时或其他原因失败, 断路器也不会打开.
      • 错误百分比阈值: 当请求总数在快照时间窗内超过了阈值, 比如发生30次调用, 15次失败, 也就是超过了50%的错误百分比, 在默认设定50%的情况下, 这时断路器就会打开了.
    3. 服务熔断步骤
      • 当满足一定阀值的时候(默认10秒内超过20个请求次数)
      • 当失败率达到一定的时候(默认10秒内超过50%请求失败)
      • 到达以上阀值,断路器将会开启
      • 当开启的时候,所有请求都不会进行转发
      • 一段时间之后(默认是5秒), 这个时候断路器是半开状态, 会让其中一个请求进行转发. 如果成功, 断路器会关闭, 若失败, 继续开启. 重复4和5
    4. 在断路器打开之后
      • 再有请求调用的时候, 将不会调用主逻辑, 而是直接调用降级fallback, 通过断路器, 实现了自动地发现错误并将降级逻辑切换为主逻辑, 减少响应延迟的效果.
      • 原来的主逻辑如何恢复?
        • 当断路器打开, 对主逻辑熔断后, hystrix会启动一个休眠时间窗, 在这个时间窗内, 降级逻辑临时成为主逻辑.
        • 当休眠时间窗到期, 断路器将进入半开状态, 释放一次请求到原来的主逻辑上, 如果此次请求正常返回, 则断路器将闭合, 主逻辑恢复. 但如果该请求依然有问题, 断路器继续进入打开状态, 休眠时间窗口重新计时.

    Hystrix工作流程

    •  官方架构图
    1. 创建HyustrixCommand对象 - 用在依赖的服务返回单个操作结果的时候
    2. 命令执行, 其中HystrixCommand实现下面两种执行方式.
      • execute(): 同步执行, 从依赖的服务返回一个单一的结果对象, 或是发生错误的时候抛出异常.
      • queue(): 异步执行, 直接返回一个Future对象, 其中包含了服务执行结束时要返回的单一结果对象.
    3. 若当前命令的请求缓存功能是被启用的, 并且该命令缓存命中, 那么缓存结果会立即以Observable对象的形式返回.
    4. 检查断路器是否为打开状态, 如果断路器是打开的, 那么Hystrix不会执行, 而是转到fallback处理逻辑(第8步), 如果断路器是关闭的, 则继续执行命令.
    5. 线程池/请求队列/信号量是否占满, 如果命令依赖服务的专有线程池和请求队列, 或信号量(不用线程池的时候)已被占满, 那么Hystrix也不会继续执行, 而是转到fallback处理逻辑(第8步).
    6. Hystrix会根据我们编写的方法来决定采取什么样的方式去请求依赖服务,
      • HystrixCommand.run(): 返回单一结果, 或抛出异常.
    7. Hystrix会将"成功", "失败", "拒绝", "超时"等信息报告给断路器, 而断路器会维护一组计数器来统计这些数据, 断路器会使用这些统计数据决定是否要打开断路器, 来对某个依赖服务的请求进行"熔断/短路".
    8. 当命令执行失败的时候, Hystrix会进入fallback尝试回退处理, 我们通常也称该操作为"服务降级", 而能够引起服务降级处理的情况有3种
      • 当命令处于"熔断/短路"状态, 断路器是打开的时候.
      • 当前命令的线程池/请求队列/信号量被占满的时候.
      • HystrixCommand.run()抛出异常的时候.
    9. 当Hystrix命令执行成功后, 它会将处理结果直接返回或以Observable的形式返回.
    • 注意: 如果我们没有为命令实现降级逻辑或在降级处理逻辑中抛出了异常, Hystrix依然会返回一个Observable对象, 但不会发射任何结果数据, 而是通过onError方法通知命令立即中断请求, 并通过onError()方法将引起命令失败的异常发送给调用者.

    Hystrix图形化Dashboard

    • 搭建仪表盘9001
    1. 新建cloud-consumer-hystrix-dashboard9001 Module
    2. pom文件
      <dependencies>
          <!--新增hystrix dashboard-->
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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>
    3. yml配置文件
      server:
        port: 9001
    4. 主启动类: 新注解@EnableHystrixDashboard
      @EnableHystrixDashboard
      @SpringBootApplication
      public class HystrixDashboard9001 {
      
          public static void main(String[] args) {
              SpringApplication.run(HystrixDashboard9001.class, args);
          }
      }
    5. 注意: 所有Provider微服务提供类(8001/8002/...)都需要监控配置依赖
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
    6. 启动该微服务, 并访问: http://localhost:9001/hystrix, 如果出现下图页面, 则表示环境搭建成功
    • 图形监控演示
    1. 新版本的Hystrix需要在主启动类 PaymentHystrixMain8001 中指定监控路径
          /**
           * 此配置是为了服务监控而配置, 与服务容错本身无关
           * ServerletReegistrationBean是因为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;
          }
      
      不指定会报错: Unable to connect to Command Metric Stream
    2. 监控测试
      • 填写监控地址
      • 图形说明
  • 相关阅读:
    P3853 [TJOI2007]路标设置
    P1182 数列分段`Section II`
    P1948 [USACO08JAN]电话线Telephone Lines
    P1541 乌龟棋
    P1005 矩阵取数游戏
    P4001 [BJOI2006]狼抓兔子
    Windows环境中Tomcat优化
    可视化GC日志工具
    垃圾回收器
    垃圾回收机制
  • 原文地址:https://www.cnblogs.com/binwenhome/p/13201708.html
Copyright © 2020-2023  润新知