Spring Cloud第六站Hystrix

  Hystrix是微服务系统架构用来处理系统延时与容错的开源库,它是微服务架构中必不可少的一个环节。可惜的是Hystrix也进入了停止更新状态(有新的开源库来顶替)。

Hystrix简介

Hystrix是什么
 Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
 ”断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack) ,而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

Hystrix能干些什么

  1. 服务降级
     通俗的讲服务降级指的是:服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback。
     哪些情况会触发服务降级:
    (1)、 程序出现异常
    (2)、 程序运行超时
    (3)、服务熔断触发了服务降级
    (4)、线程池/信号量降低也会导致服务降级

  2. 服务熔断
     当服务访问量达到最大的时候,直接拒绝访问调用服务降级的方法,返回友好提示信息。

  3. 服务限流
     对高并发访问的接口(如秒杀接口),无法通过缓存或降级来降低服务器访问量。采用服务限流的方法,来降低服务器负载。

Hystrix的服务降级

构建8001服务端
  1. 引入依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
  1. 配置yml文件
server:
  port: 8001
  servlet:
    context-path: /
spring:
  application:
    name: provider-hystrix-payment
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    # 注册进入的eureka server地址
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
  instance:
    instance-id: payment8001
    prefer-ip-address: true
  1. 编写测试类
    简单编写controller层
@RestController
@Slf4j
public class PaymentController {

    @Autowired
    private PaymentService service;

    @GetMapping(path = "/payment/hystrix/ok/{id}")
    public String getMessageOK(@PathVariable("id") String id) {
        log.info("消息返回成功");
        return service.getMessageOK(id);
    }

    @GetMapping(path = "/payment/hystrix/timeout/{id}")
    public String getMessageTimeout(@PathVariable("id") String id) throws InterruptedException {
        log.info("消息返回超时");
        return service.getMessageTimeout(id);
    }

}

简单编写service层,使用sleep模拟,处理业务流程较长时情况

@Service
public class PaymentService {
    
    public String getMessageOK(String id) {
        return "线程池" + Thread.currentThread().getName() + "返回成功:" + id;
    }

    public String getMessageTimeout(String id) throws InterruptedException {
        Thread.sleep(3000);
        return "线程池" + Thread.currentThread().getName() + "返回成功:" + id;
    }

}
  1. 访问接口
    无业务流程接口返回正常
    在这里插入图片描述
    sleep编写的模拟业务接口,3秒后也返回正常
    在这里插入图片描述
     可以看到,没有sleep方法的端口号,几乎秒相应,追加了sleep方法的方法会3秒后再响应。
     但是,这是单线程访问,如果多线程并发访问这个端口号的时候会发生什么事情呢?
     打开jmeter,压测模拟高并发访问(没有jmeter的可以去官网下载安装jmeter官网)
    设置200个线程,10次循环访问
    在这里插入图片描述
    访问,timeout接口在这里插入图片描述
    再服务并发访问超时接口的情况下,我们再访问普通接口时,会发现普通接口(/payment/hystrix/timeout/{id})也产生了访问,延时。
    原因:tomcat的默认工作线程数被打满了,没有多余的线程来分解压力和处理
构建测试用80端口客户端
  1. 引入依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
  1. 使用open feign编写远程调用接口
    主启动类新增open feign注解
@EnableFeignClients

编写open feign接口

@Component
@FeignClient(value = "provider-hystrix-payment")
public interface PaymentFeignServer {

    @GetMapping(path = "payment/hystrix/ok/{id}")
    String getMessageOK(@PathVariable("id") String id);

    @GetMapping(path = "payment/hystrix/timeout/{id}")
    String getMessageTimeout(@PathVariable("id") String id);

}
  1. 编写测试类,调用
@RestController
public class ConsumerController {

    @Autowired
    private PaymentFeignServer server;

    @GetMapping(path = "consumer/getMessageOK/{id}")
    public String getFeignMessageOk(@PathVariable("id") String id) {
        return server.getMessageOK(id);
    }

    @GetMapping(path = "consumer/getMessageTimeout/{id}")
    public String getFeignMessageTimeout(@PathVariable("id") String id) {
        return server.getMessageTimeout(id);
    }
}

 使用jMeter测试接口,开启高并发测试,效果类似于直接调用服务端情况,不仅模拟复杂业务接口,调用时间过长,不是复杂业务的接口,在实际调用的时候,因为tomcat的线程池被分配完。调用时间也过长。那么怎么解决超时连接,并返回给客户友好友情提示呢?
 HystrixCommand为你分忧

fallbackMethod为手动配置的,连接超时回调的方法
@HystrixCommand(fallbackMethod = "paymentInfo_fallbackMethod", 	commandProperties = {
            @HystrixProperty(name = 	"execution.isolation.thread.timeoutInMilliseconds", value = "1500")  //value为配置超时等待时间为1.5,方法执行时间小于1.5秒则正常运行,超过则1.5秒则调用回调方法
    })	//模拟处理业务的方法,每次处理业务时间为3秒
    public String getMessageTimeout(String id) throws InterruptedException {
        Thread.sleep(3000);
        return "线程池" + Thread.currentThread().getName() + "返回成功:" + id;
    }
    //配置的回调方法
	public String paymentInfo_fallbackMethod(String id) {
        log.info("timeoutHandler方法调用");
        return "线程池" + Thread.currentThread().getName() + "timeoutHandler:" + id;
    }

回调方法配置完毕后,并且在服务端启动类上增加@EnableCircuitBreaker注解,来激活服务降级回调方法。
在这里插入图片描述
 测试模拟的超时等待接口,会发现返回的是回调方法中的数据。
 但是,以上配置只是在服务端配置了服务降级,不满足部分时候,客户端的需求与服务端存在歧义。可能某个方法执行,服务端默认低于3秒就为正常,无需调用回调方法。可是客户端需求不一样,客户能等待的最大超时时间只能是1.5秒,这时候就需要在客户端,也配置相应的服务降级方法。
具体配置步骤与服务端类似:
1、启动类新增@EnableCircuitBreaker注解
2、service层,配置相应的回调方法

	@GetMapping(path = "consumer/getMessageTimeout/{id}")
    @HystrixCommand(fallbackMethod = "consumerHandler",
            commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})
    public String getFeignMessageTimeout(@PathVariable("id") String id) {
        return server.getMessageTimeout(id);
    }

    public String consumerHandler(String id) {
        return "调用的客户端,超时连接回调方法"+id;
    }

 现在客户端和服务端都有了自己服务降级的方法,可是问题又来了来了,在每个方法的方法头上的配置一个fallbackMethod注解,代码工作量大,代码冗余量也大。有没有一个作用域范围更大的注解呢,一个service类,配置一个降级方法。
解决方法:

  1. 在配置类上新增@DefaultProperties(defaultFallback = “global”)
  2. 在需要降级的方法上增加@HystrixCommand注解做标记
  3. 编写降级的global类
public String global() {
        return "调用了配置的全局,回调方法";
    }

 每个service类,都有了自己对应的服务降级方法,但是问题又来了,如果service很多呢,有没有一个一劳永逸的方法,给当前的微服务模块,一次性都加上服务降级的方法呢?
 答案是肯定有,这时候就需要使用到feign的相关注解:

  1. yml中引入新的配置方法
feign:
  hystrix:
    enabled: true
  1. 编写一个降级处理类实现feign调用的接口,来统一处理每个方法具体降级的返回值
@Component
public class PaymentFeignFallback implements PaymentFeignServer{	//PaymentFeignServer为实现的服务降级接口
    @Override
    public String getMessageOK(String id) {
        return "null";
    }

    @Override
    public String getMessageTimeout(String id) {
        return null;
    }
}

  1. 在feign接口配置服务降级的类
@Component
//fallback 配置的即为,步骤二中编写的继承service方法的类
@FeignClient(value = "provider-hystrix-payment", fallback = PaymentFeignFallback.class)
public interface PaymentFeignServer {

    @GetMapping(path = "payment/hystrix/ok/{id}")
    String getMessageOK(@PathVariable("id") String id);

    @GetMapping(path = "payment/hystrix/timeout/{id}")
    String getMessageTimeout(@PathVariable("id") String id);

}

服务熔断

  服务熔断,是为了应对微服务架构中,服务雪崩的情况(短时间内,对接口的调用出现异常,无法获取到正确的服务信息,避免重复错误请求,对服务器造成过大负荷,类似于保险丝熔断,但是这个“保险丝”会检测服务请求正确率,当正确率达到默认配置或手动配置的请求时,“保险丝”会自动接上,继续允许请求的访问)
  与服务降级相比较,其实只是在方法的@HystrixCommand注解中,新增了服务熔断开关的开启状态,与相关的配置。

@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//最大错误请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")//正确率,低于这个正确率时,服务会触发熔断

  具体的配置方法进行了哪些设置如下:

  1. 新增调用接口类
@GetMapping(path = "/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
        String result = service.paymentCircuitBreaker(id);
        log.info(result);
        return result;
    }
  1. 新增业务实现方法
@HystrixCommand(fallbackMethod = "circuitBreakerFallbackMethod",
            commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//最大错误请求次数
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")//正确率,低于这个正确率时,服务会触发熔断
            })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
        if (id < 0) {	//id > 0请求正常,id < 0时异常,会调用服务降级方法,是为了便于服务器的操作
            throw new RuntimeException("输入的数字必须大于0");
        }
        return "当前线程号" + Thread.currentThread().getName() + UUID.randomUUID().toString();
    }
  1. 服务降级的一些方法
	public String circuitBreakerFallbackMethod(Integer id) {
        return "请输入大于0的数字" + id;
    }

  测试:当id>0,时请求正常
在这里插入图片描述
当id<0时,请求异常,调用了降级方法
在这里插入图片描述
但现在为止,都是服务降级的内容,那之前配置的服务熔断方法去哪了呢
我们在多次使用id<0访问服务接口,当正确逐渐低于配置的60%的时候,停止使用id<0,进行访问服务器,使用id>0,进行访问服务器时,会发现仍然触发的是配置的服务降级方法
在这里插入图片描述
当我们累计成功率高于60%时,熔断器又恢复正常,我们又可以正常的访问服务端接口了。
  熔断器在这个过程中,经历了open->half open->closed的状态过程
在这里插入图片描述
 断路器触发条件:当请求次数达到阈值(当前配置10次),并且请求失败率,超过配置的成功率(当前60%)

Hystrix的一些其他配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值