Spring cloud中使用 Hystrix 请求熔断服务降级

229 阅读8分钟

Spring cloud中使用 Hystrix

前言

Hystrix
在Spring Cloud中使用了Netflix开发的Hystrix来实现熔断器。可以稍微通过几个简单的代码示例,学习Hystrix。

为什么要使用Hystrix

微服务中,⼀个请求可能需要多个微服务接⼝才能实现,会形成复杂的调⽤链路。

在微服务架构中,⼀个应⽤可能会有多个微服务组成,微服务之间的数据交互通过远程过程调⽤完成。这就带来⼀个问题,假设微服务A调⽤微服务B和微服务C,微服务B和微服务C⼜调⽤其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调⽤响应时间过⻓或者不可⽤,对微服务A的调⽤就会占⽤越来越多的系统资源,进⽽引起系统崩溃,所谓的“雪崩效应”。

如图中所示,最下游微服务响应时间过⻓,⼤量请求阻塞,⼤量线程不会释放,会导致服务器资源耗尽,最终导致上游服务甚⾄整个系统瘫痪。

解决⽅案

从可⽤性可靠性着想,为防⽌系统的整体缓慢甚⾄崩溃,采⽤的技术⼿段;
下⾯,我们介绍三种技术⼿段应对微服务中的雪崩效应,这三种⼿段都是从系统可⽤性、可靠性⻆度出发,尽量防⽌系统整体缓慢甚⾄瘫痪。

服务熔断

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

  • 1)服务熔断重点在“断”,切断对下游服务的调⽤
  • 2)服务熔断和服务降级往往是⼀起使⽤的,Hystrix就是这样。

服务降级

通俗讲就是整体资源不够⽤了,先将⼀些不关紧的服务停掉(调⽤我的时候,给你返回⼀个预留的值,也叫做兜底数据),待渡过难关⾼峰过去,再把那些服务打开。

服务降级⼀般是从整体考虑,就是当某个服务熔断之后,服务器将不再被调⽤,此刻客户端可以⾃⼰准备⼀个本地的fallback回调,返回⼀个缺省值,这样做,虽然服务⽔平下降,但好⽍可⽤,⽐直接挂掉要强(Hystrix 就是这样)

服务限流

服务降级是当服务出问题或者影响到核⼼流程的性能时,暂时将服务屏蔽掉,待⾼峰或者问题解决后再打开;但是有些场景并不能⽤服务降级来解决,⽐如秒杀业务这样的核⼼功能,这个时候可以结合服务限流来限制这些场景的并发/请求量限流措施也很多,⽐如

  • 限制总并发数(⽐如数据库连接池、线程池)
  • 限制瞬时并发数(如nginx限制瞬时并发连接数)
  • 限制时间窗⼝内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)
  • 限制远程接⼝调⽤速率、限制MQ的消费速率等

Hystrix简介

]Hystrix(豪猪----->刺),宣⾔“defend your app”是由Netflix开源的⼀个延迟和容错库,⽤于隔离访问远程系统、服务或者第三⽅库,防⽌级联失败,从⽽提升系统的可⽤性与容错性。

Hystrix主要通过以下⼏点实现延迟和容错。

请求:使⽤HystrixCommand包裹对依赖的调⽤逻辑。 微服务⽅法(@HystrixCommand 添加Hystrix控制) ——调⽤微服务

跳闸机制:当某服务的错误率超过⼀定的阈值时,Hystrix可以跳闸,停⽌请求
该服务⼀段时间。

资源隔离:Hystrix为每个依赖都维护了⼀个⼩型的线程池(舱壁模式)(或者信号
量)。如果该线程池已满, 发往该依赖的请求就被⽴即拒绝,⽽不是排队等
待,从⽽加速失败判定。

监控:Hystrix可以近乎实时地监控运⾏指标和配置的变化,例如成功、失败、
超时、以及被拒绝 的请求等。

回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执⾏回退逻辑。回
退逻辑由开发⼈员 ⾃⾏提供,例如返回⼀个缺省值。

⾃我修复:断路器打开⼀段时间后,会⾃动进⼊“半开”状态。

Hystrix熔断应⽤

被调用的微服务⻓时间没有响应,服务消费者—>被调用微服务快速失败给⽤户;提示

导入依赖

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

代码示例

开启熔断器

//开启熔断器
@EnableHystrix
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaclientApplication {

    /**
     * 实例化RestTemplate,通过@LoadBalanced注解开启均衡负载能力.
     * @return restTemplate
     */
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

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

}

这儿3个注解可以用一个来替代@SpringCloudApplication

@EnableHystrix
@EnableDiscoveryClient
@SpringBootApplication

业务实例代码

这里请求服务代码就不写了 可以查看 传送门

定义服务降级处理⽅法,并在业务⽅法上使⽤@HystrixCommandfallbackMethod属性关联到服务降级处理⽅法

容错代码

@Service
public class UserService {

    @Autowired
    RestTemplate restTemplate;

    //服务发现对象
    @Autowired
    DiscoveryClient discoveryClient;


    @HystrixCommand(fallbackMethod = "fallback")
    public User getUser(int id){

        //user微服务的名字 用来想此服务发送请求
        String  servceId = "user-server";

        String url = "http://" + servceId  + "/getUser?id=" + id;
        System.out.println(url +  " =  " +  url);
        User forObject = restTemplate.getForObject(url, User.class);

        return forObject;
    }

    /**
     * 使用@HystrixCommand注解指定当该方法发生异常时调用的方法
     * @param id id
     * @return 通过id查询到的用户
     */
    public User fallback(int id) {
        return new User(1,"test 用户 ","查询用户异常!","1");
    }

}

controller 调用

@RestController
public class GetUserController {


    @Autowired
    private UserService userService;

    @RequestMapping("get")
    public User getUser(int id){


        return userService.getUser(id);
    }
}

实体类User

public class User {

    private int id;
    private String name;
    private String sex;
    private String count;

    public User() {
    }

    public User(int id, String name, String sex, String count) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.count = count;
    }


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getCount() {
        return count;
    }

    public void setCount(String count) {
        this.count = count;
    }
}

可以看到异常容错

在这里插入图片描述

仓壁模式

由于Hystrix默认有一个线程池(10个线程)的缘故,为了所有添加了@HystrixCommand注解的方法提供 线程,如果超过了10个,其他请求就回被拒绝连接请求,对此Hystrix提供了仓壁模式。

在这里插入图片描述

如果不进⾏任何设置,所有熔断⽅法使⽤⼀个Hystrix线程池(10个线程),那么这样的话会导致问题,这个问题并不是扇出链路微服务不可⽤导致的,⽽是我们的线程机制导致的,如果⽅法A的请求把10个线程都⽤了,⽅法2请求处理的时候压根都没法去访问B,因为没有线程可⽤,并不是B服务不可⽤。

舱壁模式
在这里插入图片描述
Hystrix 不是采⽤增加线程数
⽽是单独的为每⼀个控制⽅法创建⼀个线程池的⽅式,这种模式叫做“舱壁模式",也
是线程隔离的⼿段。

    @HystrixCommand(
// 线程池标识,要保持唯⼀,不唯⼀的话就共⽤了
            threadPoolKey = "myFallBack",
// 线程池细节属性配置
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value =
                            "1"), // 线程数
                    @HystrixProperty(name = "maxQueueSize", value = "20") // 等待队列⻓度
            },
// commandProperties熔断的⼀些细节属性配置
            commandProperties = {
// 每⼀个属性都是⼀个HystrixProperty 超过2s就熔断
                    @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" // 回退⽅法
    )

上述通过注解进⾏的配置也可以配置在配置⽂件中

# 配置熔断策略:
hystrix:
  command:
    default:
      circuitBreaker:
      # 强制打开熔断器,如果该属性设置为true,强制断路器进⼊打开状态,将会拒绝所有的请求。 默认false关闭的
        forceOpen: false
          # 触发熔断错误⽐例阈值,默认值50%
        errorThresholdPercentage: 50
          # 熔断后休眠时⻓,默认值5秒
        sleepWindowInMilliseconds: 3000
        # 熔断触发最⼩请求次数,默认值是20
        requestVolumeThreshold: 2
      execution:
        isolation:
          thread:
            # 熔断超时设置,默认为1秒
            timeoutInMilliseconds: 4000




# springboot中暴露健康检查等断点接⼝
management:
  endpoints:
    web:
      exposure:
        include: "*"
  # 暴露健康接⼝的细节 请求地址  http://localhost:8080/actuator/health
  endpoint:
    health:
      show-details: always