菜鸟的springcloud学习总结(四):服务降级

菜鸟的springcloud学习总结(四):服务降级

  • 说明
  • 一、服务降级
  • 二、Hystrix
    • (1)服务端降级
    • (2)客户端服务降级
    • (3)服务熔断
    • (4)流量监控

说明

更新时间:2020/10/01 18:04,更新到了Hystrix

本文主要对springcloud中的服务降级进行学习与记录,主要偏向于实战,本文会持续更新,不断地扩充

本文仅为记录学习轨迹,如有侵权,联系删除

一、服务降级

菜鸟的springcloud学习总结(四):服务降级_第1张图片
现在进行到了服务降级这一块,这一块有一些基本的概念需要了解

服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C有调用其他的微服务,这就是所谓的”扇出”,如扇出的链路上某个微服务的调用响应式过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统雪崩,所谓的”雪崩效应”

服务降级
服务降级是指 当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(退路)错误处理信息,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务。

简单理解就是在服务器压力大的情况下,先停掉一些非核心的服务,直接返回错误信息,把资源让给核心的服务,这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。

服务熔断
服务熔断是应对雪崩效应的一种微服务链路保护机制。例如在高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。同样,在微服务架构中,熔断机制也是起着类似的作用。当调用链路的某个微服务不可用或者响应时间太长时,会进行服务熔断,不再有该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。

简单理解就是当服务链路的某一个微服务不可用的情况下,直接停止该节点服务的调用,快速返回错误的信息

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

简单理解就是当有大量的请求打进服务的时候,服务检测到这样大的流量,并且对流量进行限制

服务熔断和服务降级的区别

服务熔断 服务降级
服务熔断一般是某个服务(下游服务)故障引起 服务降级一般是从整体负荷考虑
依赖的下游服务故障触发熔断,避免引发本系统崩溃;系统自动执行和恢复 服务分优先级,牺牲非核心服务(不可用),保证核心服务稳定;从整体负荷考虑;

二、Hystrix

Hystrix是Netflix开源的一款容错框架,包含常用的容错方法:线程池隔离、信号量隔离、熔断、降级回退。在高并发访问下,系统所依赖的服务的稳定性对系统的影响非常大,依赖有很多不可控的因素,比如网络连接变慢,资源突然繁忙,暂时不可用,服务脱机等。我们要构建稳定、可靠的分布式系统,就必须要有这样一套容错方法。

下面开始实战

(1)服务端降级

pom
‘先创建一个支付服务模块,命名为cloud-provider-hystrix-payment8003,里面引入坐标依赖如下

    <dependencies>

        <!--引入公共包-->
        <dependency>
            <groupId>com.zsc</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!-- hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <!--springboot starter启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--eureka client-->
        <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-actuator</artifactId>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>


    </dependencies>

主要是eureka坐标依赖和hystrix坐标依赖,然后是主启动类的注解添加,主要添加两个,一个用于Eureka,一个用于Hystrix激活
菜鸟的springcloud学习总结(四):服务降级_第2张图片
application.yml
创建yml配置文件进行配置

server:
  port: 8003

spring:
  application:
    name: cloud-provider-hystrix-payment

eureka:
  client:
    #将自己注册进eureka服务中心
    register-with-eureka: true
    fetch-registry: true

    #对应的eureka服务端的地址
    service-url:
      #集群的情况下,服务的入住
      #defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7002.com:7002/eureka/
      # 单机版
      defaultZone: http://eureka7001.com:7001/eureka/


  instance:
    #actuator微服务信息完善,修改该服务的名称
    instance-id: payment-hystrix8003
    #访问路径可以显示ip地址
    prefer-ip-address: true

service层
然后是服务层的方法定义,在这一层上将会对一些方法进行降级处理,这里先简单讲一下什么样的条件下会触发降级处理,一个是响应的时间超过设置好的时间就会触发降级处理,这样可以避免长时间的响应导致资源被拖死,还有一个是出现异常会触发降级处理
菜鸟的springcloud学习总结(四):服务降级_第3张图片
这里定义了一个方法paymentInfoTimeout,并且通过@HystrixCommand注解添加了降级处理,设置了最长能等待的响应时间为2秒,一旦响应的时间超过2秒就会跳到对应的降级方法。同时异常也会触发降级方法

这样会有一个问题,如果每个方法都做降级处理的话,会有大量的代码冗余,于是出现了全局降级处理,通过在服务层对应的类上加一个注解@DefaultProperties,这样,里面的所有的方法都会有对应的全局降级方法,如果是特定的方法需要特殊处理,也可以用上面@HystrixCommand注解进行特殊处理
菜鸟的springcloud学习总结(四):服务降级_第4张图片
下面给出服务层的全部代码

@Service
@DefaultProperties(defaultFallback = "paymentGlobalFallback")
public class PaymentService {

    @HystrixCommand(fallbackMethod = "paymentInfoTimeoutHandler",commandProperties = {
            //表示如果调用paymentInfoTimeout方法,响应的时间超出2000毫秒(2s)就会触发降级方法,
            // 跳转到paymentInfoTimeoutHandler方法
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
    })
    public String paymentInfoTimeout(Integer id){

        /**
         * 异常也会触发降级方法
         */
        //int a = 10/0;

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:"+Thread.currentThread().getName()+", this is paymentInfoTimeout: "+id+",没想到吧,老子花了3秒时间!!!";
    }


    @HystrixCommand//默认全局降级方法
    public String paymentInfoOk(Integer id){
        return "线程池:"+Thread.currentThread().getName()+", this is paymentInfoOk: "+id.toString();
    }


    @HystrixCommand//默认全局降级方法
    public String paymentInfoException(){
        int a = 10/0;
        return "this is runtimeException";
    }

    @HystrixCommand//默认全局降级方法
    public String hello(){
        return "this is hello";
    }


    /**
     * 全局的降级方法
     * @return
     */
    public String paymentGlobalFallback(){
        return "this is paymentGlobalFallback(全局的降级方法)";
    }

    /**
     * paymentInfoTimeout特定的降级方法
     * @param id
     * @return
     */
    public String paymentInfoTimeoutHandler(Integer id){
        return "this is paymentInfoTimeoutHandler(paymentInfoTimeout对应的降级方法)";
    }

}

controller层
控制层直接调用服务层接口,没什么好说的

@RestController
@RequestMapping("/payment")
public class PaymentController {
    @Autowired
    private PaymentService paymentService;

    @GetMapping("/paymentInfoOk/{id}")
    public String paymentInfoOk(@PathVariable("id") Integer id){
        String result = paymentService.paymentInfoOk(id);
        System.out.println("*****************result = " + result);
        return result;
    }

    @GetMapping("/paymentInfoTimeout/{id}")
    public String paymentInfoTimeout(@PathVariable("id") Integer id){
        String result = paymentService.paymentInfoTimeout(id);
        System.out.println("*****************result = " + result);
        return result;
    }


    @GetMapping("/paymentInfoException")
    public String paymentInfoException(){
        String s = paymentService.paymentInfoException();
        return s;
    }

    @GetMapping("/hello")
    public String hello(){
        String hello = paymentService.hello();
        return hello;
    }

}

运行项目进行测试,这里测试服务降级,可以不用开eureka服务端
在这里插入图片描述

菜鸟的springcloud学习总结(四):服务降级_第5张图片

(2)客户端服务降级

对于服务端的服务降级也可以用服务端降级的方式进行降级处理,但一般不那么做,而是用面向接口的方式进行服务降级

创建客户端模块
创建客户端(消费者)模块cloud-consumer-openfeign-hystrix-order80,用于调用上面的服务端,同样pom引入坐标

    <dependencies>

        <!--引入公共包-->
        <dependency>
            <groupId>com.zsc</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!-- hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <!--openFeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--eureka client-->
        <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-actuator</artifactId>
        </dependency>


        <!--springboot starter启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>


    </dependencies>

客户端要调用服务,要用到openfeign,所以这里重点引入openfeign、eureka和hystrix坐标依赖,然后配置yml文件

server:
  port: 80

spring:
  application:
    name: cloud-consumer-openfeign-hystrix-order80


eureka:
  client:
    #将自己注册进eureka服务中心
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #集群的情况下,服务的入住
      defaultZone: http://eureka7001.com:7001/eureka/
ribbon:
  #建立连接后从服务器读取到的可用资源所用的时间
  ReadTimeout: 5000
  #建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ConnectTimeout: 5000


#开启hystrix
feign:
  hystrix:
    enabled: true

重要的一点,主启动类添加上对应的注解
菜鸟的springcloud学习总结(四):服务降级_第6张图片
服务调用
服务的调用使用openfeign进行调用
菜鸟的springcloud学习总结(四):服务降级_第7张图片
controller层
菜鸟的springcloud学习总结(四):服务降级_第8张图片
这个在服务调用篇章说过了,就不过多解释了

服务降级
客户端的服务降级虽然可以采用跟服务端的降级方式,但一般不用,而是用新建一个类,并且继承service层的接口,在里面实现降级方法,使用的时候用@FeignClient注解调用即可
菜鸟的springcloud学习总结(四):服务降级_第9张图片
@FeignClient里面的fallback属性表示,如果客户端调用服务端,由于各种原因客户端没有拿到响应的数据,就会触发降级方法,通过fallback标注的类,找同名的实现类方法作为降级方法,下面给出这两个的代码

//value是要调用的哪一个服务,fallback是服务降级触发的降级方法
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = OpenfeignHystrixServiceFallback.class)
@Service
public interface OpenfeignHystrixService {

    @GetMapping("/payment/paymentInfoOk/{id}")
    public String paymentInfoOk(@PathVariable("id") Integer id);

    @GetMapping("/payment/paymentInfoTimeout/{id}")
    public String paymentInfoTimeout(@PathVariable("id") Integer id);

    @GetMapping("/payment/paymentInfoException")
    public String paymentInfoException();

    @GetMapping("/payment/hello")
    public String hello();
}

@Component
public class OpenfeignHystrixServiceFallback implements OpenfeignHystrixService {
    @Override
    public String paymentInfoOk(Integer id) {
        return "(客户端)this is paymentInfoOk 降级方法";
    }
    @Override
    public String paymentInfoTimeout(Integer id) {
        return "(客户端)this is paymentInfoTimeout 降级方法";
    }
    @Override
    public String paymentInfoException() {
        return "(客户端)this is paymentInfoException 降级方法";
    }
    @Override
    public String hello() {
        return "(客户端)this is hello 降级方法";
    }
}

然后是controller层的接口定义,这个就正常写接口即可
菜鸟的springcloud学习总结(四):服务降级_第10张图片
启动项目,由于这次客户端调用服务端,所以需要启动eureka服务端注册中心
菜鸟的springcloud学习总结(四):服务降级_第11张图片
菜鸟的springcloud学习总结(四):服务降级_第12张图片

(3)服务熔断

菜鸟的springcloud学习总结(四):服务降级_第13张图片

服务熔断的概念这里就再重复,上面有,这里直接进入实战,还是再这个项目上进行服务熔断处理,在服务层再添加一个方法如下
菜鸟的springcloud学习总结(四):服务降级_第14张图片
这里主要讲解这个方法上的注解
菜鸟的springcloud学习总结(四):服务降级_第15张图片
这个注解上面开启了熔断机制,意思就如图上面所说的开启了断路器后,当10秒内,5个请求的进来后,失败率达到60%以上的时候触发熔断机制,触发后短时间内该方法不可用,当又有请求进来后,成功请求次数越来越多的时候会恢复该链路,即链路恢复并且上面还有fallback服务降级处理,可以这样理解,当达到熔断条件后,先进行服务降级,触发降级方法,然后服务熔断,之后服务再慢慢恢复

控制层写一个接口测试一下熔断机制
菜鸟的springcloud学习总结(四):服务降级_第16张图片
启动项目
在这里插入图片描述
菜鸟的springcloud学习总结(四):服务降级_第17张图片
总结
断路器的打开和关闭,是按照一下5步决定的
1,并发此时是否达到我们指定的阈值
2,错误百分比,比如我们配置了60%,那么如果并发请求中,10次有6次是失败的,就开启断路器
3,上面的条件符合,断路器改变状态为open(开启)
4,这个服务的断路器开启,所有请求无法访问
5,在我们的时间窗口期,期间,尝试让一些请求通过(半开状态),如果请求还是失败,证明断路器还是开启状态,服务没有恢复
如果请求成功了,证明服务已经恢复,断路器状态变为close关闭状态

(4)流量监控

当微服务越来越多的情况下,就必须要对这些服务进行监控,下面直接进行服务的监控

创建流量监控模块cloud-hystrixdashboard-9001,引入pom坐标

    <dependencies>

        <!--引入公共包-->
        <dependency>
            <groupId>com.zsc</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!--hystrix dashboard-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>

        <!-- hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <!--监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--springboot starter启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

重点是hystrix dashboard仪表盘依赖,简单配置一下该项目的端口号
在这里插入图片描述
主启动类上添加注解
在这里插入图片描述
启动该项目
菜鸟的springcloud学习总结(四):服务降级_第18张图片
添加服务监控
要监控哪个服务就在哪个服务里面引入actuator依赖,这里以上面创建的cloud-provider-hystrix-payment8003项目为例,监控该项目

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>

然后在cloud-provider-hystrix-payment8003项目的主启动类中添加监控配置
菜鸟的springcloud学习总结(四):服务降级_第19张图片

    /**
     * 此配置是为了服务监控而配置的,与服务容错本身无关,Springboot升级后留下的坑
     * @return
     */
    @Bean
    public ServletRegistrationBean getServlet(){
        HystrixMetricsStreamServlet hystrixMetricsStreamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(hystrixMetricsStreamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }

两个项目启动后,访问http://localhost:9001/hystrix,并且在那里数据要监控的服务路径进行监控
菜鸟的springcloud学习总结(四):服务降级_第20张图片
菜鸟的springcloud学习总结(四):服务降级_第21张图片
对该仪表盘的解读
菜鸟的springcloud学习总结(四):服务降级_第22张图片

你可能感兴趣的:(springcloud,Hystrix)