微服务之三:Hystrix(1)

一: 概述

很多系统中应考虑横向扩展,单点故障问题。在出现故障时,为了较少故障的影响,保障集群的高可用,应增加保护机制,Hystrix就是其中的一种保护机制

  • Hystrix通过添加延迟阈值以及容错的逻辑,来帮助我们控制分布式系统间组件的交互。
  • Hystrix通过隔离服务间的访问点、停止他们之间的级联故障,提供可回退操作来实现容错。

Hystrix的功能:

  • 当所依赖的网络服务发生延迟或者失败时,对访问的客户端程序进行保护。
  • 在分布式系统中,停止级联故障
  • 网络服务恢复正常时,可以快速恢复客户端的访问能力
  • 调用失败时执行服务回退
  • 可支持实时监控、报警、和其他操作

1.1 Hystrix程序

1.2 Hystrix的使用

内部流程
从之前的例子看,当服务器出现无响应现象的时候,Hystrix会自动使用容错机制,看似简单,其实有一套较为复杂的执行逻辑

  • 在命令开始执行时,会做一些准备工作(如:为命令创建相应的线程池等)
  • 判断是否打开了缓存,打开了缓存就直接查找缓存并返回结果
  • 判断断路器是否打开,如果打开了,就表示链路不可以用,直接执行回退方法
  • 判断线程池、信号量等条件(如:线程池超负荷,则返回回退方法,否则,就去执行命令的内容)
  • 执行命令,判断是否要对断路器进行处理,执行完成后,如果满足一定条件,则需要开启断路器
    整个流程最主要的点,就是在于断路器是否被打开

1.3 命令执行

  • toObservable:返回一个最原始的可观察的实例,是一个RxJava的类,可观察命令的执行过程,并且将执行的信息转递给订阅者(命令不会马上执行,只有当返回的可观察实例被订阅之后,才会真正执行)
  • observe: 用toObservable获得最原始的可观察实例后,再使用一个replay subject作为原始toObservable的订阅者(会立刻执行
  • queue
  • execute

1.4 配置属性

1.5 回退

列举三种情况下触发回退

  • 断路器被打开
  • 线程池、队列、信号量满载
  • 实际执行命令失败
package com.atm.cloud.config;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;

public class MyTimeOutConfig extends HystrixCommand<String> {

    public MyTimeOutConfig() {
        // 第二种方法:
        // super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));

        // 超时时间设置成2s
        super(Setter.withGroupKey(
                HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionTimeoutInMilliseconds(2000)));

        // 第一种方法:设置超时时间(设置成2s)
        HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(
                20000);
    }

    @Override
    protected String run() throws Exception {
        // 手动延迟3s
        // Thread.sleep(3000);
        System.out.println("执行命令...");
        return "Success";
    }

    // 回退方法
    @Override
    protected String getFallback() {
        System.out.println("执行回退...");
        // return super.getFallback();
        return "Fallback...";
    }

}

如果断路器打开,则会执行回退方法,
如果断路器打开但没有提供回退方法,系统则会报异常

1.6 回退的模式

Hystrix回退机制比较灵活,
你可以在A命令的回退方法中执行B命令,如果B命令执行也失败,同意也可以触发B命令的回退

1.7 断路器开启

断路器一旦开启,就会直接

  • 调用回退方法,
  • 不再执行命令,
  • 也不会更新链路的健康状况

断路器开启要满足两个条件

  • 整个链路达到一定阈值,默认情况下,10秒内产生超过20次请求,则符合第一个条件
  • 满足第一个条件下,如果请求的错误百分比大于阈值,则会打开断路器,默认为50%
package com.atm.cloud.open;

import com.netflix.config.ConfigurationManager;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandMetrics.HealthCounts;
import com.netflix.hystrix.HystrixCommandProperties;

public class CloseMain {

    public static void main(String[] args) throws Exception {
        // 10秒内大于3次请求,满足第一个条件
        ConfigurationManager
                .getConfigInstance()
                .setProperty(
                        "hystrix.command.default.circuitBreaker.requestVolumeThreshold",
                        3);
        boolean isTimeout = true;
        for (int i = 0; i < 10; i++) {// 发送10次请求,一开始均为失败,则会打开断路器
            TestCommand c = new TestCommand(isTimeout);// 总会超时
            c.execute();
            // 输出健康信息
            HealthCounts hc = c.getMetrics().getHealthCounts();
            System.out.println("断路器状态:" + c.isCircuitBreakerOpen() + ", 请求数量:"
                    + hc.getTotalRequests());
            if (c.isCircuitBreakerOpen()) {// 如果断路器打开
                isTimeout = false;//
                System.out.println("============  断路器打开了,等待休眠期结束");
                Thread.sleep(6000);// 等待休眠期结束,6s之后尝试性发一次请求
            }
        }
    }

    static class TestCommand extends HystrixCommand<String> {

        private boolean isTimeout;

        public TestCommand(boolean isTimeout) {
            super(Setter.withGroupKey(
                    HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                    .andCommandPropertiesDefaults(
                            HystrixCommandProperties.Setter()
                                    .withExecutionTimeoutInMilliseconds(500)));// 500毫秒没有响应,则打开断路器
            this.isTimeout = isTimeout;
        }

        @Override
        protected String run() throws Exception {
            if (isTimeout) {
                Thread.sleep(800);// 方法执行时间为800毫秒
            } else {
                Thread.sleep(200);
            }
            return "";
        }

        @Override
        protected String getFallback() {
            return "fallback";
        }
    }
}

1.8 断路器关闭

  • 当断路器打开的时候,不再执行命令,直接进行回退方法,这段时间称为休眠期,默认时间为5s
  • 休眠期结束之后,会尝试性执行一次命令。
    • 此时,断路器状态处于半开状态,尝试执行成功之后,就会关闭断路器并且清空健康信息,
    • 如果失败,断路器会继续保持打开的状态

1.9 隔离机制

命令的真正执行,除了断路器要关闭外,还需要再过一关

  • 执行命令的线程池或者信号量是否满载
    • 如果满载,命令就不会执行,而是直接出发回退

Hystrix提供了两种隔离策略

  • thread(线程,消耗可能大点,异步超时)
  • semaphore(信号量,不支持超时、异步)

1.9.1 编写命令

package com.atm.cloud;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;

public class MyCommand extends HystrixCommand<String> {

    public int index;

    public MyCommand(int index) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory
                .asKey("ExampleGroup")));
        this.index = index;
    }

    @Override
    protected String run() throws Exception {
        Thread.sleep(500);
        System.out.println("run(),当前索引:" + index);
        return "success";
    }

    @Override
    protected String getFallback() {
        System.out.println("getFallback(),当前索引:" + index);
        return "fallback";
    }

}

  • 线程隔离测试,编写测试类
package com.atm.cloud;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;

public class MyCommand extends HystrixCommand<String> {

    public int index;

    public MyCommand(int index) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory
                .asKey("ExampleGroup")));
        this.index = index;
    }

    @Override
    protected String run() throws Exception {
        Thread.sleep(500);
        System.out.println("run(),当前索引:" + index);
        return "success";
    }

    @Override
    protected String getFallback() {
        System.out.println("getFallback(),当前索引:" + index);
        return "fallback";
    }

}

  • 信号量隔离测试
package com.atm.cloud;

import com.netflix.config.ConfigurationManager;
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;

public class SemaphoreMain {

    public static void main(String[] args) throws Exception {
        // 信号量策略,默认最大并发数10
        ConfigurationManager.getConfigInstance().setProperty(
                "hystrix.command.default.execution.isolation.strategy",
                ExecutionIsolationStrategy.SEMAPHORE);

        // 设置最大并发数为2
        ConfigurationManager
                .getConfigInstance()
                .setProperty(
                        "hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests",
                        2);

        for (int i = 0; i < 6; i++) {
            final int index = i;
            Thread t = new Thread() {
                public void run() {
                    MyCommand c = new MyCommand(index);
                    c.execute();
                }
            };
            t.start();
        }

        Thread.sleep(5000);
    }
}

1.10 请求缓存

在一起请求中,多个地方调用同一个接口,可考虑使用缓存
需要用到CommandKey
合并请求,请求缓存,在一次请求的过程中才能实现,因此需要先初始化请求上下文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值