服务高可用-Hystrix

引入Hystrix

分布式微服务场景下,调用服务调用依赖服务,是通过RPC进行调用的。在高并发访问下,依赖服务的稳定性与否对调用服务影响非常大,即依赖服务存在着很多不可控的问题,如:网络延迟、服务繁忙、服务阻赛、服务不可用等。
User-request服务依赖I服务出现不可用,但是其他依赖仍然可用。当依赖I服务 阻塞时,大多数服务器的线程池就出现阻塞(BLOCK),影响整个线上服务的稳定性。

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

1、Hystrix简介

Hystrix:通过服务熔断(也可以称为断路)、降级、限流(隔离)、异步RPC等手段控制依赖服务的延迟与失败。

Circuit Breaker :熔断器

熔断只是作用在服务调用这一端,只需改consumer端。
1)熔断器开关相互转换的逻辑
: a.服务的健康状况 = 请求失败数 / 请求总数.
: b.熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值比较决定的
: : b1.关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈 值, 开关则切换为打开状态
: : b2.打开状态, 经过一段时间后, 熔断器会自动进入半开状态, 这时熔断器只允许一个请求通过. 当该请求调用成功时, 熔断器恢复到关闭状态. 若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过
: c.保证服务调用者在调用异常服务时, 快速返回结果, 避免大量的同步等待
: d.在一段时间后继续侦测请求执行结果, 提供恢复服务调用的可能
2)参数设值
: a.circuitBreaker.requestVolumeThreshold //滑动窗口的大小,默认为20
: b.circuitBreaker.sleepWindowInMilliseconds //过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟
: c.circuitBreaker.errorThresholdPercentage //错误率,默认50%每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。

降级:fallback

当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值

Isolation:限流(隔离)

线程隔离:
即将每个依赖服务分配独立的线程池进行资源隔离, 从而避免服务雪崩
线上建议线程池不要设置过大,否则大量堵塞线程有可能会拖慢服务器
信号隔离:
用于限制并发访问,防止阻塞扩散, 与线程隔离最大不同在于执行依赖代码的线程依然是请求线程(该线程需要通过信号申请)
如果客户端是可信的且可以快速返回,可以使用信号隔离替换线程隔离,降低开销。

2、Hystrix执行流程

在这里插入图片描述
①、每次调用创建一个新的HystrixCommand,把依赖调用封装在run()方法中.
②、执行execute()/queue做同步或异步调用.
③、判断熔断器(circuit-breaker)是否打开,如果打开跳到步骤8,进行降级策略,如果关闭进入步骤.
④、判断线程池/队列/信号量是否跑满,如果跑满进入降级步骤8,否则继续后续步骤.
⑤、调用HystrixCommand的run方法.运行依赖逻辑
: 5a:依赖逻辑调用超时,进入步骤8.
⑥:判断逻辑是否调用成功
: 6a:返回成功调用结果
:: 6b:调用出错,进入步骤8.
⑦:计算熔断器状态,所有的运行状态(成功, 失败, 拒绝,超时)上报给熔断器,用于统计从而判断熔断器状态.
⑧:getFallback()降级逻辑.
: 以下四种情况将触发getFallback调用:
: : : (1):run()方法抛出非HystrixBadRequestException异常。
: : : (2):run()方法调用超时
: : : (3):熔断器开启拦截调用
: : : (4):线程池/队列/信号量是否跑满
: 8a:没有实现getFallback的Command将直接抛出异常
: 8b:fallback降级逻辑调用成功直接返回
: 8c:降级逻辑调用失败抛出异常
⑨:返回执行成功结果

3、Hystrix参数介绍

超时时间(默认1000ms,单位:ms)
(1)hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
在调用方配置,被该调用方的所有方法的超时时间都是该值,优先级低于下边的指定配置
(2)hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds
在调用方配置,被该调用方的指定方法(HystrixCommandKey方法名)的超时时间是该值

线程池核心线程数
hystrix.threadpool.default.coreSize(默认为10)

Queue
(1)hystrix.threadpool.default.maxQueueSize(最大排队长度。默认-1,使用SynchronousQueue。其他值则使用 LinkedBlockingQueue。如果要从-1换成其他值则需重启,即该值不能动态调整,若要动态调整,需要使用到下边这个配置)
(2)hystrix.threadpool.default.queueSizeRejectionThreshold(排队线程数量阈值,默认为5,达到时拒绝,如果配置了该选项,队列的大小是该队列)
注意:如果maxQueueSize=-1的话,则该选项不起作用

断路器
(1)hystrix.command.default.circuitBreaker.requestVolumeThreshold(当在配置时间窗口内达到此数量的失败后,进行短路。默认20个)
For example, if the value is 20, then if only 19 requests are received in the rolling window (say a window of 10 seconds) the circuit will not trip open even if all 19 failed.
简言之,10s内请求失败数量达到20个,断路器开。
(2)hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds(短路多久以后开始尝试是否恢复,默认5s)
(3)hystrix.command.default.circuitBreaker.errorThresholdPercentage(出错百分比阈值,当达到此阈值后,开始短路。默认50%)

fallback
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests
(调用线程允许请求HystrixCommand.GetFallback()的最大数量,默认10。超出时将会有异常抛出。注意:该项配置对于THREAD隔离模式也起作用)

整合Hystrix

1、技术框架

项目框架:Spring boot
分布式协调:Dubbo
中间件:Zookeeper
日志工具:Sf4j
构建工具:Maven
开发工具:IDEA

2、环境准备

2.1 添加依赖

  <!--hystrix -->
  <dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-core</artifactId>
    <version>${hystrix-version}</version>
  </dependency>
  <dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-metrics-event-stream</artifactId>
    <version>${hystrix-version}</version>
  </dependency>
  <dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-javanica</artifactId>
    <version>${hystrix-version}</version>
  </dependency>
  <dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-servo-metrics-publisher</artifactId>
    <version>${hystrix-version}</version>
  </dependency>

hystrix-version:1.5.18,添加需要的版本。

2.2 Hystrix配置

Hystrix配置在服务调用者
在这里插入图片描述
@Configuration
public class HystrixConfig {

//设置Aspect
@Bean
public HystrixCommandAspect hystrixCommandAspect(){
    return new HystrixCommandAspect();
}

//注入servlet
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
    return new ServletRegistrationBean(new HystrixMetricsStreamServlet(),"/hystrix.stream");
}

}
hystrixCommandAspect()配置hystrixCommandAspect为Spring 对象,hystrixMetricsStreamServlet()配置hystrix-dashboard的监控。

2.3 Hystrix代码实现

在调用依赖服务的方法上加上@HystrixCommand注解,并配置相关参数。

@Service
public class ComsumerServiceImpl implements ConsumerService {
private static final Logger LOGGER = LoggerFactory.getLogger(ComsumerServiceImpl.class);

@Autowired
private OrderService orderService;

private AtomicInteger successCount = new AtomicInteger(0);
private AtomicInteger failCount = new AtomicInteger(0);


@Override
@HystrixCommand(groupKey="BestGroup", commandKey = "BestCommand",threadPoolKey = "best", fallbackMethod = "getNameFallback",
        commandProperties = {
                //@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),//指定隔离策略为信号量SEMAPHORE,默认THREAD
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),//指定多久超时,单位毫秒。超时进fallback
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//一个统计窗口内熔断触发的最小个数/10s,默认是20
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),//熔断多少秒后去尝试请求,默认是5000ms
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "10"),//失败率达到多少百分比后熔断,默认值50
                @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "50")//fallback最大并发度,默认10
        },
        threadPoolProperties = {
                @HystrixProperty(name = "coreSize", value = "10"),// 设置线程池的core size,这是最大的并发执行数量,默认是10
                @HystrixProperty(name = "maxQueueSize", value = "5"),// 最大队列长度。设置BlockingQueue的最大长度,默认是-1
                @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                @HystrixProperty(name = "queueSizeRejectionThreshold", value = "3")// 此属性设置队列大小拒绝阈值 - 即使未达到maxQueueSize也将发生拒绝的人为最大队列大小。
                // 此属性存在,因为BlockingQueue的maxQueueSize不能动态更改,我们希望允许您动态更改影响拒绝的队列大小
                // 默认值:5, 注意:如果maxQueueSize == -1,则此属性不适用
        })
public String getName(String id) {
    LOGGER.info("consumer->service->thread:{},id:{}", Thread.currentThread().getName(),id);
    String result = orderService.getOrder(id);
    int i = successCount.incrementAndGet();
    LOGGER.info("成功次数:{}" , i);
    return result;
}

public String getNameFallback(String id) {
    LOGGER.info("consumer->service->fallback->thread:{},id:{}", Thread.currentThread().getName(),id);
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    int i = failCount.incrementAndGet();
    LOGGER.info("失败次数==={}" , i);
    return "回退";
}

}
以下是模拟依赖服务超时的场景:
1、配置服务调用方法的超时时间为1000毫秒
@HystrixProperty(name = “execution.isolation.thread.timeoutInMilliseconds”, value = “1000”)
注意:此处依赖服务超时和Dubbo超时会取小者,此处dubbo服务的超时设置为2000毫秒
<dubbo:reference id=“orderService” retries=“0” timeout=“2000” interface=“com.bestpay.service.OrderService” check=“false” filter=“traceFilter”/>
2、配置断路器打开的阈值为50%
@HystrixProperty(name = “circuitBreaker.errorThresholdPercentage”, value = “50”)
3、配置服务提供方方法随机超时,超过1000毫秒
public class OrderServiceImpl implements OrderService {

private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);

@Override
public String getOrder(String orderid) {
    LOGGER.info("thread:" + Thread.currentThread().getName());
       try{ boolean flag = new Random().nextBoolean();
        if(flag){
            Thread.sleep(2000);
        }else{
            Thread.sleep(500);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    LOGGER.info("provider->service->param:{}", orderid);
    return orderid + ",OK";
}

}
3、启动相关服务,使用Jmeter模拟并发,监控Hystrix断路器的状态。
在这里插入图片描述
错误率()达到50%以上的时候,断路器状态打开,之后的请求在调用依赖服务的时候被短路,服务降级,对调用服务起到保护的作用,提高调用的可用性。
以上只演示Hystrix断路器熔断的场景,关于Hystrix的隔离的情况,可自行研究。
备注:错误率计算如下:Timed-out + Threadpool Rejected + Failuer / Total
在这里插入图片描述

作者简介:就职于甜橙金融信息技术部,负责服务端开发,专注于微服务、分布式、性能调优、高可用,欢迎各位同仁沟通交流。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值