Spring Cloud学习(Hoxton.SR1版) Hytrix

916 阅读5分钟

一、引言

1. 服务雪崩

在分布式项目中,随着项目规模不断扩张,服务之间调用也变得越来越复杂,链路也变得越来越长,假设各服务之间的调用关系如下图:

当服务C因为请求流量过大响应过慢或自身出现问题时,若服务B仍不断调用服务C,那么自身的资源也将会被一点点耗尽,从而导致服务B也变为不可用,进而导致服务A也无法正常使用。 因为一个服务失败,从而导致整个链路的服务都失败,这种现象被称为服务雪崩。 为了避免服务雪崩,通常我们会采用服务降级服务熔断这两种方式来解决。

2. 服务降级

那么,什么是服务降级呢? 简单来说就是在请求响应过慢或下游系统不可用时,主动调用一些降级逻辑,迅速返回,避免长时间的等待。 ##3. 服务熔断 其实服务熔断也是一种服务降级的策略,与服务降级不同是,服务熔断会直接放弃调用响应过慢或不可用的下游系统,从而保证自身可用性,等到目标系统好转时重新恢复调用。 这套熔断机制的常用的是断路器模式

  • 一开始处于Closed状态,当错达到阈值时,转化为Open
  • 当超过设置的reset timeout时,状态会转变为Half Open,此时会尝试调用,一旦成功,便会重新转为Closed

二、Hytrix

1. Hytrix简介

Hystrix可通过添加延迟公差和容错逻辑来控制分布式服务之间的交互。 通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的。 需要注意的一点是,Hytrix已经进入了维护模式。

2. Hytrix的作用

  • 提供依赖项的延迟和失败的控制保护。
  • 防止分布式系统的级联故障。
  • 快速失败和恢复。
  • 回退并在可能的情况下降级。
  • 近乎实时的监视,警报和操作控制。

3. Hytrix设计原则

  • 防止任何单个依赖项耗尽所用户线程。
  • 减少负载并快速失败,而不是排队。
  • 在可行的情况下提供备用,以保护用户免受故障的影响。
  • 使用隔离技术(例如隔板,泳道和断路器模式)来限制任何一种依赖关系的影响。
  • 通过近实时指标,监控和警报确保故障及时发现。
  • 以低延迟传播配置更改优化恢复时间,并支持动态属性更改进行实时操作修改。
  • 防止整个依赖客户端执行失败,而不仅仅是网络通信失败。

4. Hytrix实现原理

  • 通过命令模式将外部调用包装在HystrixCommandHystrixObservableCommand对象中,并在单独线程中执行。
  • 为每个依赖项维护一个小的线程池(或信号灯),如果已满,发往该依赖项的请求将立即被拒绝,而不是排队。
  • 记录失败(客户端抛出的异常),超时和线程拒绝。
  • 设置失败阈值,在超过该阈值时打开短路器,拒绝请求。
  • 请求失败,被拒绝,超时或短路时执行回退逻辑。
  • 实时监控指标和配置更改。

5. 流程图

Hystrix的路程图如下:

具体执行流程:

  1. 构造HystrixCommandHystrixObservableCommand 对象,前者为阻塞的,后者为非阻塞;
  2. 执行HystrixCommandqueue().get()方法,或HystrixObservableCommand toObserve()方法获取响应,其实toObserve()是执行其父类的方法;
  3. 如果请求启用了缓存且请求响应在缓存中,则返回缓存中的响应;
  4. 检查断路器是否开启,若开启则调到8,否则进入5;
  5. 检查线程池和队列是否已满,若满了则走到8;
  6. 执行run()construct()方法,返回单个响应或Observable;
  7. 记录统计信息,根据这些信息来判断何时开启断路器;
  8. 执行配置的fallback方法;
  9. 返回。

3. Hystrix例子

本文的例子是Spring Boot + Spring Cloud Feign + Spring Cloud Eureka。

3.1 创建服务注册中心,

  1. 引入依赖:
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 创建applicaiton.yml配置文件
server:
  port: 7001

spring:
  application:
    name: cloud-eureka-server

eureka:
  instance:
    appname: 127.0.0.1
  client:
    register-with-eureka: false # false:
    fetch-registry: false # false:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka
  1. 创建启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain.class, args);
    }
}

3.2 创建服务提供者

  1. 引入依赖
<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-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 创建配置文件
server:
  port: 8001

eureka:
  instance:
    instance-id: payment8001
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka,
  1. 创建service和controller,本文只是为了学习Hystrix,做个简单的例子,不涉及数据库操作。 controller层
@RestController
@RequestMapping("/payment")
public class PaymentController {

    @Resource
    private IPaymentService paymentService;

    @RequestMapping("/success")
    public String successMethod() {
        return paymentService.successMethod();
    }

    @RequestMapping("/fail")
    public String getTimeout(@RequestParam("id")Integer id){
        return paymentService.failMethod(id);
    }
}

service层

public interface IPaymentService {
     String failMethod(Integer id);
}

@Service
public class PaymentServiceImpl implements IPaymentService{

    @Override
    @HystrixCommand(fallbackMethod="fallBackMethod", commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")

    })
    public String failMethod(Integer id) {
       if (id < 0) {
           throw new RuntimeException("=====id不能为负数=====");
       }
        return "fail";
    }

    public String fallBackMethod(Integer id) {
        return "fallBackMethod:" + id;
    }
}

circuitBreaker.enabled:启用熔断器; circuitBreaker.requestVolumeThreshold:请求次数阈值,小于该阈值时,即便其他条件满足也不会开启断路器; circuitBreaker.sleepWindowInMilliseconds:断路器打开后,重新尝试等待的时间; circuitBreaker.errorThresholdPercentage:错误次数的百分比。 更详细的配置可以在HystrixCommandProperties类中查看。

4. 创建消费者

  1. 引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

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

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 创建配置文件
server:
  port: 80

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka

ribbon:
  ReadTimeout: 3000
  ConnectTimeout: 3000
logging:
  level:
    com.fegin.service.IPaymentFeign: debug
# 开启hystrix
feign:
  hystrix:
    enabled: true

创建启动类

@SpringBootApplication
// 开启Feign
@EnableFeignClients 
// 开启Hystrix 
@EnableCircuitBreaker
public class Applicaiton {
    public static void main(String[] args) {
        SpringApplication.run(Applicaiton.class, args);
    }
}

创建controller,并配置服务降级,这种方式可以配置当前类的全局降级方法。

@RestController
@RequestMapping("/cons")
@DefaultProperties(defaultFallback = "global_fialMethod")
public class ConsumerController {

    @Resource
    IPaymentFeign paymentFeign;

    @GetMapping("/fail")
    @HystrixCommand
    public String get(){
        return paymentFeign.failMethod();
    }

    public String global_fialMethod() {
        return  "global_fialMethod";
    }
}

另一种配置,因为整合了OpenFeign,而OpenFeign自己支持Hystrix,可以直接实现接口,当服务调用失败是,会走本地实现类的方法,让编码更优雅。

@Component
@FeignClient(value = "CLOUD-PAYMENT-HYSTRIX",fallback = PaymentImpl.class)
public interface IPaymentFeign {
    @RequestMapping("/payment/failMethod")
    String failMethod();
}

@Service
public class PaymentImpl implements IPaymentFeign{

    @Override
    public String failMethod() {
        return "local failMethod";
    }
}

本文是本人学习中的总结与笔记,如有不足,希望各路大神能够支出。

参考

www.cnblogs.com/aiqiqi/p/11…
github.com/Netflix/Hys…