Spring Cloud Hystrix具体使用

823 阅读4分钟

引入依赖

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

并在主函数加上@EnableHystrix注解 测试不添加以上注释,好像也没毛病 我们首先使用继承HystrixCommand的形式,来实现可以更直观的看出使用细节

第一种使用方式:使用继承HystrixCommand的形式

public class MyCommand extends HystrixCommand<Integer> {
    //注入自己的相关服务
    @Autowired
    private Dome dome;

    //构造函数
    protected MyCommand(Dome dome) {
        //dome是相应的类,queryByOrderId是其中具体的方法
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("dome"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("queryByOrderId"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withCircuitBreakerRequestVolumeThreshold(10)//至少有10个请求,熔断器才进行错误率的计算
                        .withCircuitBreakerSleepWindowInMilliseconds(5000)//熔断器中断请求5秒后会进入半打开状态,放部分流量过去重试
                        .withCircuitBreakerErrorThresholdPercentage(50)//错误率达到50开启熔断保护
                        .withExecutionTimeoutEnabled(true))
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties
                        .Setter().withCoreSize(10)));
        this.dome = dome;
    }

    @Override
    protected Integer run() throws Exception {
        return null;
    }

    @Override
    protected Integer getFallback() {
        return -1;
    }

开始运行

public void testQueryByOrderIdCommand() {
        Integer r = new MyCommand(dome).execute();
}

这里有几种运行方式,下面说明一哈

execute()

以同步堵塞方式执行run(),只支持接收一个值对象。hystrix会从线程池中取一个线程来执行run(),并等待返回值。

queue()

以异步非阻塞方式执行run(),只支持接收一个值对象。调用queue()就直接返回一个Future对象。可通过 Future.get()拿到run()的返回结果,但Future.get()是阻塞执行的。若执行成功,Future.get()返回单个返回值。当执行失败时,如果没有重写fallback,Future.get()抛出异常。

observe()

事件注册前执行run()/construct(),支持接收多个值对象,取决于发射源。调用observe()会返回一个hot Observable,也就是说,调用observe()自动触发执行run()/construct(),无论是否存在订阅者。 如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run();如果继承的是HystrixObservableCommand,将以调用线程阻塞执行construct()。

observe()使用方法:
1、调用observe()会返回一个Observable对象
2、调用这个Observable对象的subscribe()方法完成事件注册,从而获取结

toObservable()

事件注册后执行run()/construct(),支持接收多个值对象,取决于发射源。调用toObservable()会返回一个cold Observable,也就是说,调用toObservable()不会立即触发执行run()/construct(),必须有订阅者订阅Observable时才会执行。 如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run(),调用线程不必等待run();如果继承的是HystrixObservableCommand,将以调用线程堵塞执行construct(),调用线程需等待construct()执行完才能继续往下走。

toObservable()使用方法:

1、调用observe()会返回一个Observable对象
2、调用这个Observable对象的subscribe()方法完成事件注册,从而获取结果

需注意的是,HystrixCommand也支持toObservable()和observe(),但是即使将HystrixCommand转换成Observable,它也只能发射一个值对象。只有HystrixObservableCommand才支持发射多个值对象。 下面一图展示了几种方式的联系

20190520080007488.png execute()实际是调用了queue().get() queue()实际调用了toObservable().toBlocking().toFuture() observe()实际调用toObservable()获得一个cold Observable,再创建一个ReplaySubject对象订阅Observable,将源Observable转化为hot Observable。 因此调用observe()会自动触发执行run()/construct()。

  • 以上继承的方式可以很明确的看出具体细节,但是相对来说代码敲得比较繁琐,一般可以使用官方提供的注解来使用,以下看方式二

方式二:使用@HystrixCommand配置相关内容

@SneakyThrows
    @GetMapping("/getOther")
    @HystrixCommand(fallbackMethod = "fallback",
            // 线程池标识
            threadPoolKey = "getTodayStatisticB",
            threadPoolProperties = {
                    @HystrixProperty(name="coreSize",value = "1"),  // 这个就是咱们那个线程池core线程核心数
                    @HystrixProperty(name="maxQueueSize",value="100") //这个是队列大小
            },
            // 使用commandProperties 可以配置熔断的一些细节信息
            commandProperties = {
            // 类似kv形式
            //这里这个参数意思是熔断超时时间2s,表示过了多长时间还没结束就进行熔断
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000"),
            // 当遇到失败后,开启一个11s的窗口
            @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "11000"),
            // 最小请求数
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "3"),
            // 失败次数占请求的50%
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"),
            // 跳闸后 活动窗口配置 这里配置了10s
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000")
    })
public String getOther(@RequestParam(required = false) String userId){
        //暂停2秒
        Thread.sleep(2000);
        System.out.println("当前线程为:"+Thread.currentThread().getName());
        String res = restTemplate.getForObject("http://CLIENT2-SERVICE/client2/get",String.class);
        return res;
    }

使用配置说明

commandProperties配置熔断细节
execution.isolation.thread.timeoutInMilliseconds为超时时间
metrics.rollingStats.timeInMilliseconds当遇到失败后,开启一个一定时间的窗口(详情见滑动窗口限流)
circuitBreaker.requestVolumeThreshold最小请求数
circuitBreaker.errorThresholdPercentage失败比例
circuitBreaker.sleepWindowInMilliseconds跳闸后滑动窗口时间

测试

按照上面代码所示配置,我们开始测试 使用Thread.sleep(2000);休眠两秒调用方法getOther,发现成功进入熔断 如上代码所示,我们设置的失败占比为50%,我们代码中写的线程暂停2秒,使用postman调用可以 发现前两次次进入熔断, 时间均为2秒左右

clipboard2.png 第三次进入熔断 时间为

clipboard3.png 可以发现我们已经进入失败窗口 此时等待11秒后再次请求,又开始重新判断,可以看见我们前面配置的失败滑动窗口时间 为11s

方式三:同样我们也可以使用yml配置的形式使用

clipboard4.png