断路器:Hystrix 的使用

引入依赖

<dependency> <!-- 引入eureka client -->
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency> <!-- hystrix -->
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency> <!-- openfeign -->
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

开启熔断

在启动类上加上注解

@EnableCircuitBreaker // 开启断路器

@EnableFeignClients // 开启 Feign

添加配置

开启Fegin的hystrix

OpenFeign 是自带 Hystrix 的,但是默认没有开启,在 application.properties 中添加以下配置开启 Hystrix

# 开启feign下面的hystrix功能
feign.hystrix.enabled: true

对应开启配置的源码在 FeignClientsConfiguration.java中

// org.springframework.cloud.openfeign.FeignClientsConfiguration.HystrixFeignConfiguration
@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {

   @Bean
   @Scope("prototype")
   @ConditionalOnMissingBean
   @ConditionalOnProperty(name = "feign.hystrix.enabled")
   public Feign.Builder feignHystrixBuilder() {
      return HystrixFeign.builder();
   }

}

配置Hystrix参数

# 开启全局超时
hystrix.command.default.execution.timeout.enabled=true
# 开启访问缓存功能
hystrix.command.default.requestCache.enabled=true
# 隔离策略,默认线程隔离
hystrix.command.default.execution.isolation.strategy=THREAD

###########################  线程隔离  ##############################
# 线程隔离的超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
# 超时以后中止线程
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true
# 取消的时候终止线程
hystrix.command.default.execution.isolation.thread.interruptOnFutureCancel=true
####################################################################


###########################  信息量隔离(与线程隔离不能同时存在)  ###
# 最大并发请求数
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=100
####################################################################

# 开启熔断的功能
hystrix.command.default.circuitBreaker.enabled=true
# 在一定的时间窗口内,请示5个以上以后,才开始进行熔断判断
hystrix.command.default.circuitBreaker.requestVolumeThreshold=5
# 当熔断开启以后,经过多少秒再进入半开状态
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=15000
# 超过50%的失败请求,则熔断开关开启
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
# 强制开启熔断开关
hystrix.command.default.circuitBreaker.forceOpen=false
hystrix.command.default.circuitBreaker.forceClosed=false

# 设置一个rolling window被划分的时间
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=20000
# 设置一个rolling window被划分的数量
hystrix.command.default.metrics.rollingStats.numBuckets=10

这些设置是默认全局的,如果想对单个方法进行设置则要把 default 换成 HystrixCommand.commandKey() ,如果注解上没有指定commandKey,默认是 类名#方法名(参数类型) 例如:

MyService#retry(Integer)

断路器调用

断路器的三种状态:

  • 关闭状态

断路器处理关闭状态,统计调用失败次数,在一段时间内达到一定的阀值后断路器打开。

  • 打开状态

对方法调用直接返回失败错误,不发生真正的方法调用,设置了一个重置时间,在重置时间结束后,断路器来到半开状态。

  • 半开状态

此时允许进行方法调用,当调用成功了(或者成功达到一定的比例),关闭断路器,否则认为服务没有恢复,重新打开断路器。

编写Feign调用接口 FeignService.java

@FeignClient(name = "feign-client", fallback = Fallback.class)
public interface FeignService {
    @GetMapping("/fallback")
    String fallback();
    
    @GetMapping("/continueFallback")
    String continueFallback();
}

编写降级类 Fallback.java

@Slf4j
@Component
public class Fallback implements FeignService {
   

    @Override
    public String fallback() {

        log.info("降级成功");
        return "降级成功";
    }
    
     SecureRandom random = new SecureRandom();
     @Override
    // 连续降级
    @HystrixCommand(fallbackMethod = "fallback2")
    public String continueFallback() {
    
        if (random.nextBoolean()) {
            return "第一次降级返回的结果:success-1";
        }
    
        String s = "第一次降级";
        log.error(s);
        throw new RuntimeException("first fallback");
    }
    
    @HystrixCommand(fallbackMethod = "fallback3")
    public String fallback2() {
    
        if (random.nextBoolean()) {
            return "第二次降级返回的结果:success-2";
        }
        log.error("第二次降级");
        throw new RuntimeException("fallback again");
    }
    
    public String fallback3() {
    
        if (random.nextBoolean()) {
            log.error("第三次降级");
            return "第三次降级返回的结果:success-3";
        }
        log.error("第三次降级 失败");
        throw new RuntimeException("第三次降级 失败");
    }

}

提供对外访问接口 Controller.java

@Slf4j
@RestController
public class Controller {
    @Autowired
    private FeignService           feignService;
    
    @GetMapping("/fallback")
    public String randomError() {

        return feignService.fallback();
    }

    @GetMapping("/continueFallback")
    public String continueFallback() {

        return feignService.continueFallback();
    }
    
}

请求合并

Hystrix 还提供了请求合并的功能。多个请求被合并为一个请求进行一次处理,可以有效减少网络通信和线程池资源。

它提供两种方式进行请求合并:request-scoped 收集一个 HystrixRequestContext 中的请求集合成一个批次;而 globally-scoped 将多个 HystrixRequestContext 中的请求集合成一个批次,这需要应用的下游依赖能够支持在一个命令调用中处理多个 HystrixRequestContext。

1. 通过注解方式进行请求合并

单个请求需要使用 @HystrixCollapser 注解修饰,并指明 batchMethod,可以设置合并请求的一些参数,这里我们设置请求合并的周期为100秒。由于请求合并中不能同步等待结果,所以单个请求返回的结果为Future,即需要异步等待结果。

batchMethod 方法的参数只能是List,不能是Set,因为Set是无序的,在合并请求的时候无法对应。

@HystrixCollapser(batchMethod = "getInstanceByIds",
        // 设置请求合并的周期为100秒
        collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds", value = "10000") })
public Future<String> getInstanceById(String id) {

    return null;
}

@HystrixCommand
public List<String> getInstanceByIds(List<String> ids) {

    log.info("start batch");
    List<String> list = new ArrayList<>();
    for (String id : ids) {
        list.add(id);
    }
    return list;
}

调用

@GetMapping("/batch")
public List<String> batch() throws InterruptedException, ExecutionException {
    // 请求开始前需要为请求初始化 HystrixRequestContext, 用于在同一请求中的线程间共享数据,封装指请求,请求结束前需要关闭
    HystrixRequestContext context = HystrixRequestContext.initializeContext();
    Future<String> fc = collapserService.getInstanceById("c");
    Future<String> fa = collapserService.getInstanceById("a");
    Future<String> fd = collapserService.getInstanceById("d");

    TimeUnit.MILLISECONDS.sleep(1000);
    Future<String> fb = collapserService.getInstanceById("b");

    List<String> list = Arrays.asList(fa.get(), fb.get(), fc.get(), fd.get());

    context.close();

    return list;
}

如果在请求合并周期内直接调用 Future#get 方法阻塞等待同步结果,将会强行合并请求。

2. 继承 HystrixCollapser

请求合并命令同样也可以通过自定义的方式实现,只需要继承 HystrixCollapser 抽象类。

public class MyHystrixCollapserCommand extends HystrixCollapser<List<String>, String, Integer> {

    Integer requestId;

    public MyHystrixCollapserCommand(Integer requestId) {

        // 需要指定 CollapserKey, 用来标记被合并请求的键值
        super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("myHystrixCollapserCommand")));
        this.requestId = requestId;
    }

    @Override
    public Integer getRequestArgument() {

        return this.requestId;
    }

    @Override
    protected com.netflix.hystrix.HystrixCommand<List<String>> createCommand(Collection<CollapsedRequest<String, Integer>> collapsedRequests) {

        // 合并请求参数
        List<Integer> ids = collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList());
        HystrixCommandGroupKey batchGroup = HystrixCommandGroupKey.Factory.asKey("requestIdBatchGroup");

        return new HystrixCommand<List<String>>(batchGroup) {

            @Override
            protected List<String> run() throws Exception {

                // 发起远程批量请求,这里不是重点,忽略
                List<String> list = ids.stream().map(String::valueOf).collect(Collectors.toList());

                return list;
            }
        };
    }

    @Override
    protected void mapResponseToRequests(List<String> batchResponse,
                                         Collection<CollapsedRequest<String, Integer>> collapsedRequests) {

        int count = 0;
        for (CollapsedRequest<String, Integer> request : collapsedRequests) {
            // 分拆对应请求的响应结果
            request.setResponse(batchResponse.get(count++));
        }
    }
}

在Controller中使用

@GetMapping("/myBatch")
public List<String> myBatch() throws InterruptedException, ExecutionException {

    // 请求开始前需要为请求初始化 HystrixRequestContext, 用于在同一请求中的线程间共享数据,封装指请求,请求结束前需要关闭
    HystrixRequestContext context = HystrixRequestContext.initializeContext();

    MyHystrixCollapserCommand c1 = new MyHystrixCollapserCommand(1);
    MyHystrixCollapserCommand c2 = new MyHystrixCollapserCommand(2);
    MyHystrixCollapserCommand c3 = new MyHystrixCollapserCommand(3);
    MyHystrixCollapserCommand c4 = new MyHystrixCollapserCommand(4);

    Future<String> f1 = c1.queue();
    Future<String> f2 = c2.queue();
    Future<String> f3 = c3.queue();

    TimeUnit.MILLISECONDS.sleep(1000);
    Future<String> f4 = c4.queue();

    List<String> list = Arrays.asList(f1.get(), f2.get(), f3.get(), f4.get());

    context.close();

    return list;
}

效果和 @HystrixCollapser 一样。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值