雪崩效应
在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问A服务,而A服务需要调用B
服务,B服务需要调用C服务,由于网络原因或者自身的原因,如果B服务或者C服务不能及时响应,A服
务将处于阻塞状态,直到B服务C服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕,
导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难
性的严重后果,这就是服务故障的“雪崩”效应。

SpringCloud之Hystrix断路器(六)_断路器

雪崩是系统中的蝴蝶效应导致其发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方
法响应变慢,亦或是某台机器的资源耗尽。从源头上我们无法完全杜绝雪崩源头的发生,但是雪崩的根
本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。

服务隔离
顾名思义,它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。
当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体
的系统服务。

熔断降级
熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。在互联网系统中,当下游服务因访问压
力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这
种牺牲局部,保全整体的措施就叫做熔断。

SpringCloud之Hystrix断路器(六)_spring_02

所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的
fallback回调,返回一个缺省值。 也可以理解为兜底

 服务限流

限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说
系统的吞吐量是可以被测算的,为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流
量并采取少量措施以完成限制流量的目的。比方:推迟解决,拒绝解决,或者者部分拒绝解决等等。

Hystrix介绍

SpringCloud之Hystrix断路器(六)_限流_03

Hystrix 是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失
败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。
包裹请求:使用 HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用
了设计模式中的“命令模式”。
跳闸机制:当某服务的错误率超过一定的阈值时, Hystrix可以自动或手动跳闸,停止请求该服务一段时间。
资源隔离: Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,
发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
监控: Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员
自行提供,例如返回一个缺省值。
自我修复:断路器打开一段时间后,会自动进入 “半开”状态。

整合Hystrix

  • order-service

    • pom.xml

    •       <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
              </dependency>
    • web层

       1 @RestController
       2 @RequestMapping("/api/v1/order")
       3 public class OrderController {
       4  5  6     @Autowired(required = false)
       7     private ProductOrderServiceImpl productOrderService;
       8  9 10     @RequestMapping("/save")
      11     @HystrixCommand(fallbackMethod="saveOrderFail")
      12     public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId){
      13 14         Map<String, Object> data = new HashMap<>();
      15         data.put("code", 0);
      16         data.put("data", productOrderService.save(userId, productId));
      17         return  data;
      18     }
      19 20     //注意,方法签名一定要要和api方法一致
      21     public Object saveOrderFail(int userId,int productId){
      22 23         Map<String, Object> msg = new HashMap<>();
      24         msg.put("code", -1);
      25         msg.put("msg", "抢购人数太多,您被挤出来了,稍等重试");
      26         return msg;
      27     }
      28 29 30 }
    • application.yml

       1 server:
       2   port: 8781
       3  4  5 #指定注册中心地址
       6 eureka:
       7   client:
       8     serviceUrl:
       9       defaultZone: http://localhost:8761/eureka/
      10 11 #服务的名称
      12 spring:
      13   application:
      14     name: order-service
      15 16 ###配置请求超时时间
      17 hystrix:
      18   command:
      19     default:
      20       execution:
      21         isolation:
      22           thread:
      23              timeoutInMilliseconds: 7000
      24 ribbon:
      25 ##指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。
      26   ReadTimeout: 3000
      27 ##指的是建立连接后从服务器读取到可用资源所用的时间。
      28   ConnectTimeout: 3000
      29 30 #自定义负载均衡策略
      31 #product-service:
      32 #  ribbon:
      33 #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  • product-service

    • service层

       1 package com.topcheer.producerservice.controller;
       2  3 import com.topcheer.producerservice.domain.Product;
       4 import com.topcheer.producerservice.service.ProductService;
       5 import org.springframework.beans.BeanUtils;
       6 import org.springframework.beans.factory.annotation.Autowired;
       7 import org.springframework.beans.factory.annotation.Value;
       8 import org.springframework.web.bind.annotation.*;
       9 10 import java.util.concurrent.TimeUnit;
      11 12 @RestController
      13 @RequestMapping("/api/v1/product")
      14 public class ProductController {
      15 16 17 18     @Value("${server.port}")
      19     private String port;
      20 21     @Autowired
      22     private ProductService productService;
      23 24     /**
      25      * 获取所有商品列表
      26      * @return
      27      */
      28     @RequestMapping("list")
      29     public Object list(){
      30         return productService.listProduct();
      31     }
      32 33 34     /**
      35      * 根据id查找商品详情
      36      * @param id
      37      * @return
      38      */
      39     @RequestMapping("find")
      40     public Object findById(int id){
      41 42         try {
      43             TimeUnit.SECONDS.sleep(2);
      44         } catch (InterruptedException e) {
      45             e.printStackTrace();
      46         }
      47         Product product = productService.findById(id);
      48 49         Product result = new Product();
      50         BeanUtils.copyProperties(product,result);
      51         result.setName( result.getName() + " data from port="+port );
      52         return result;
      53     }
      54 55 }
      启动类也要添加注解
    • SpringBootApplication
      @EntityScan("com.topcheer.order.entity")
      @EnableFeignClients
      @EnableHystrix
      public class OrderApplication {
      
          /**
           * 使用spring提供的RestTemplate发送http请求到商品服务
           *      1.创建RestTemplate对象交给容器管理
           *      2.在使用的时候,调用其方法完成操作 (getXX,postxxx)
           * * @LoadBalanced : 是ribbon提供的负载均衡的注解
           */
          @LoadBalanced
          @Bean
          public RestTemplate restTemplate() {
              return new RestTemplate();
          }
      
          public static void main(String[] args) {
              SpringApplication.run(OrderApplication.class,args);
          }
      }

       注:也可以卸载整个类上,代表整个类都会走这个异常

            SpringCloud之Hystrix断路器(六)_服务器_04

  • 结果:

    当前面的ribbon配置3秒的时候,可以直接返回结果。

    当前面的ribbon配置2秒的时候,会被HystrixCommand里面的异常方法捕获到。

SpringCloud之Hystrix断路器(六)_服务器_05