Spring Cloud系列教程(八):服务保护断路器Hystrix(Finchley版本)

一、断路器的产生背景

在微服务架构场景中,通常会有很多层的服务互相调用。如果某一个底层服务出现问题,由于网络原因或者其他一些因素,有可能出现服务不可用的情况,当某个服务出现问题时,其他服务如果继续调用这个服务,就有可能出现线程阻塞,但如果同时有大量的请求,就会造成线程资源被用完,这样就可能会导致系统服务瘫痪,由于服务间会相互调用,很容易造成服务连环雪崩效应,从而故障会被向上传播响应给用户。我们需要一种机制,当底层服务不可用时,可以阻断故障的传播。这就是断路器的作用。他是系统服务稳定性的最后一重保障,是在微服务架构场景中必不可少的一部分。

二、什么是Hystrix

HystrixSpringCloud中的一个断路器组件。Hystrix是由国外知名的视频网站Netflix所开源,被SpringCloud所整合使用。他的功能是,当对某个服务的调用在一定的时间内(默认10s,由metrics.rollingStats.timeInMilliseconds配置),有超过一定次数(默认20次,由circuitBreaker.requestVolumeThreshold参数配置)并且失败率超过一定值(默认50%,由circuitBreaker.errorThresholdPercentage配置),该服务的断路器会打开,实现服务降级, 常见的是返回一个由开发者设定的fallback回调函数,响应友好提示给客户端。 例如:某些知名的抢票网站,在高并发场景下,如果系统人数较多,会实现服务降级、熔断、隔离,响应一个友好提示给用户。

某某某,您好,当前系统人数较多,请稍后重试,给您带来的不便,敬请谅解!!!

三、Hystrix服务降级、熔断、隔离

在上面常见的微服务架构场景中,断路器的出现很大程度上是应用于解决微服务雪崩效应,起到整个微服务服务链路的保护作用,解决服务雪崩效应可基于Hystrix的服务降级+服务熔断,服务隔离实现

  • 服务降级: 在高并发场景下,请求过多,为了防止用户一直等待响应结果,微服务链路容易造成连环雪崩效应,这时候会使用服务降级方式,返回一个友好提示给客户端,具体做法是调用fallBack()本地方法,不再去处理业务请求,目的是为了用户体验的同时,保护了整个微服务链路。

  • 服务熔断: 服务熔断机制的目的是为了保护服务,在高并发的情况下,如果请求达到一定极限(可以自己设置阔值)如果流量超出了设置阈值,然后直接拒绝访问,保护当前服务。使用服务降级方式返回一个友好提示,服务熔断和服务降级一起使用。 服务熔断原理跟家庭中的保险丝是一样。

  • 服务隔离: 分为线程池隔离和信号量隔离

    1.线程池隔离:

    每个服务接口,都有自己独立的线程池,每个线程池互不影响,Hystrix中默认开启线程池隔离。

    2.信号量隔离:
    使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返 回成功后计数器-1。

  • 3.服务限流:
    服务限流就是对接口访问进行限制,常用服务限流算法令牌桶、漏桶。计数器也可以进行粗暴限流实现。

    以上几点概念摘自于: SpringCloud学习笔记 -----Hystrix 服务降级、熔断机制、服务隔离

四、Hystrix使用方式

  • Hystrix有两种使用方式:分别采用注解形式( @HystrixCommand)接口形式(基于Feign)

  • 启动类加入@EnableHystrix注解,表示启用Hystrix断路器

五、实现思路

继前面章节基于 Spring Cloud系列教程(五) - 服务消费者Feign+Ribbon(Finchley版本) 的源码,继续使用服务提供者springcloud-app-member工程,端口为8762和注册中心springcloud-eureka-server工程,端口为9000,消费者消费服务springcloud-feign-client工程,端口为8787。 然后修改springcloud-feign-client工程的代码,其他两个工程不变,springcloud-feign-client工程需要加入Hystrix断路器组件。分别采用注解形式和接口形式配置Hystrix断路器服务保护。,下面先贴一下项目完整结构图:
Spring Cloud系列教程(八):服务保护断路器Hystrix(Finchley版本)_第1张图片

六、环境搭建

1. Hystrix依赖

 <!-- springcloud整合hystrix服务保护组件   -->
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>

2. springcloud-feign-client整体依赖

  <!--SpringBoot依赖版本-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.thinkingcao</groupId>
    <artifactId>springcloud-feign-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-feign-client</name>
    <description>SpringCloud整合Feign客户端组件消费服务</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <!--声明管理SpringCloud版本依赖信息-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- SpringBootWeb组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- springcloud整合eureka客户端组件   -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- springcloud整合Feign客户端组件   -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!-- springcloud整合hystrix服务保护组件   -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <!--lombok代码简化工具-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

3. application.yml

##=========服务消费者-订单服务配置========
#服务端口号
server:
  port: 8787
#定义服务名称(服务注册到eureka名称)
spring:
  application:
    name: app-thinkingcao-feign
#在此指定服务注册中心地址,将当前订单服务注册到eureka注册中心上
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:9000/eureka
    #启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true
    register-with-eureka: true
    #是否需要从eureka上获取注册信息
    fetch-registry: true
    #表示eureka client间隔多久去拉取服务器注册信息,默认为30秒
    registry-fetch-interval-seconds: 10

  ##心跳检测与续约时间(测试环境和本地开发环境将值设置小一点,保证服务关闭后,注册中心能够及时踢出)
  instance:
    #客户端向Eureka注册中心发送心跳的时间间隔,单位为秒(默认为30s),(客户端会按照此规则向Eureka服务端发送心跳检测包)
    lease-renewal-interval-in-seconds: 2
    #Eureka注册中心在收到客户端最后一次心跳之后等待的时间上限,单位为秒(默认为90s),超过时间则剔除(客户端会按照此规则向Eureka服务端发送心跳检测包)
    lease-expiration-duration-in-seconds: 2

##启用hystrix
feign:
  hystrix:
    #打开Feign自带的断路器
    enabled: true

4. 启动类加@EnableHystrix

@EnableHystrix : 表示启用Hystrix断路器组件相关功能

package com.thinkingcao.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class AppFeignClient {

    public static void main(String[] args) {
        SpringApplication.run(AppFeignClient.class, args);
    }
}

5. 注解形式配置Hystrix断路器

语法: @HystrixCommand(fallbackMethod= "hystrixMethod")fallbackMetho为服务降级的回调方法,同时@HystrixCommand默认开启了服务的熔断、降级、隔离(线程池隔离)

分别新建两个方法:

  1. 没有使用断路器解决服务雪崩效应方法(getOrderToMemberInfoNoHystrix),因为这个是基于Feign客户端调用服务提供者(springcloud-app-member),当停掉springcloud-app-member服务时,直接导致服务不可用;
  2. 使用断路器解决服务雪崩效应方法(getOrderToMemberInfoForHystrix),因为这个是基于Feign客户端调用服务提供者(springcloud-app-member),当停掉springcloud-app-member服务时,由于采用了直接Hystrix断路器实现服务降级、熔断处理,不会导致服务不可用,而是执行服务降级方法,返回友好提示给前端用户;
  • ApiOrderController
package com.thinkingcao.api.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.thinkingcao.api.Feign.IFeignClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @desc:
 * @author: cao_wencao
 * @date: 2020-02-22 12:30
 */
@RestController
public class ApiOrderController {

    @Autowired(required=false)
    private IFeignClientService feignClientService;

    //没有使用断路器解决服务雪崩效应方法
    @RequestMapping(value = "/getOrderToMemberInfoNoHystrix",method = RequestMethod.GET)
    public String getOrderToMemberInfoNoHystrix(@RequestParam("userName") String userName){
        String memberInfo = feignClientService.getOrderToMemberInfo(userName);
        return memberInfo;
    }

    //使用断路器解决服务雪崩效应方法
    @HystrixCommand(fallbackMethod= "excuteHystrixHandle")
    @RequestMapping(value = "/getOrderToMemberInfoForHystrix",method = RequestMethod.GET)
    public String getOrderToMemberInfoForHystrix(@RequestParam("userName") String userName){
        String memberInfo = feignClientService.getOrderToMemberInfo(userName);
        return memberInfo;
    }

    //服务进入保护时,回调方法
    public String excuteHystrixHandle(String userName) {
        return  userName+"您好,当前系统人数较多,请稍后重试,给您带来的不便,敬请谅解!!!";
    }
}

6. 接口形式配置Hystrix断路器

在微服务项目架构中,以及日常开发中,使用基于接口形式较多,如果以接口形式配置Hystrix,可基于Feign客户端配置;

- 服务提供方加一个服务降级的回调方法excuteHystrixHandler

package com.thinkingcao.api.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @desc:   会员服务接口
 * @author: cao_wencao
 * @date: 2020-02-19 18:12
 */
@RestController
public class MemberController {

    @Value("${server.port}")
    private String serverPort;

    //会员服务接口
    @RequestMapping("/getMember")
    private String getMember(@RequestParam("userName") String userName) {
        return "我是会员服务,订单服务调用会员服务成功啦, 端口号为: " + serverPort +"——用户名: "+ userName;
    }

    //服务提供方-服务降级方法
    @RequestMapping("/excuteHystrixHandler")
    private String excuteHystrixHandler(){
        return "您好,当前系统人数较多,请稍后重试,给您带来的不便,敬请谅解!!!";
    }
}

- 服务消费方加一个Feign接口,声明执行Hystrix降级方法

package com.thinkingcao.api.Feign;

import com.thinkingcao.api.service.impl.GetHystrixResultImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @desc:   接口形式配置Hystrix,fallback服务出现熔断时,需要降级到此回调方法,响应给用户
 * @author: cao_wencao
 * @date: 2020-02-25 12:53
 */
@FeignClient(value = "app-thinkingcao-member",fallback = GetHystrixResultImpl.class)
public interface IExcuteHystrixHandler {

    //获取Hystrix降级熔断执行回调方法的结果
    @RequestMapping("/excuteHystrixHandler")
    public String getHystrixInfo();
}

Feign接口实现类:

package com.thinkingcao.api.service.impl;

import com.thinkingcao.api.Feign.IExcuteHystrixHandler;
import org.springframework.stereotype.Component;

/**
 * @desc: GetHystrixResultImpl注入到Spring容器,交给Spring管理
 * @author: cao_wencao
 * @date: 2020-02-25 12:59
 */
@Component
public class GetHystrixResultImpl implements IExcuteHystrixHandler {
    @Override
    public String getHystrixInfo() {
        return  "您好,当前系统人数较多,请稍后重试,给您带来的不便,敬请谅解!!!";
    }
}

七、测试

  • 启动Eureka注册中心AppEurekaServer
  • 启动服务提供者AppMemberProvider
  • 启动服务消费者`AppFeignClient

Spring Cloud系列教程(八):服务保护断路器Hystrix(Finchley版本)_第2张图片

  • 在上述3个服务正常启动完成后,稍等片刻,服务注册需要时间,先访问URL:
    http://127.0.0.1:8787/getOrderToMemberInfoForHystrix?userName=“Thinkingcao”,响应结果正常:

我是会员服务,订单服务调用会员服务成功啦, 端口号为: 8762——用户名: “Thinkingcao”

Spring Cloud系列教程(八):服务保护断路器Hystrix(Finchley版本)_第3张图片

  • 停掉服务提供者工程,然后再次访问URL:http://127.0.0.1:8787/getOrderToMemberInfoForHystrix?userName=“Thinkingcao”,响应结果走服务降级方法:

“Thinkingcao”您好,当前系统人数较多,请稍后重试,给您带来的不便,敬请谅解!!!

Spring Cloud系列教程(八):服务保护断路器Hystrix(Finchley版本)_第4张图片

  • 访问没有使用Hystrix的方法,报500异常,这种方式太不友好了:
    添加链接描述Spring Cloud系列教程(八):服务保护断路器Hystrix(Finchley版本)_第5张图片

八、源码

1. 项目源码: https://github.com/Thinkingcao/SpringCloudLearning/tree/master/springcloud-hystrix

九、SpringCloud系列教程

下一篇: Spring Cloud系列教程(九):服务网关Zuul(Finchley版本)

SpringCloud教程汇总: Spring Cloud系列教程(汇总篇):专栏汇总篇(持续更新中)

你可能感兴趣的:(Spring,Cloud2.x系列教程)