Spring Cloud Hystrix(服务容错保护)(3)

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37598011/article/details/82667881

1.请求合并

在微服务的架构中的依赖通常通过远程调用来实现,而远程调用最常出现的问题是通信消耗与连接数占用。Hystrix提供了HystrixCollapser来实现请求合并,以减少通信消耗和线程数的占用。

HystrixCollapser实现了在HystrixCommand之前放置一个合并处理器,将处于一个很短的时间窗内对同一依赖服务的多个请求进行整合并以批量方式发起请求的功能(服务提供方也需要提供相应的批量实现接口)。

这里可以使用以继承HystrixCollapser和注解的方式实现,但是吧用代码实现实在是太麻烦了在项目中应该很少会这么用,所以这里我采用注解的形式实现。

@Service
public class HelloService {

    private final Logger logger=Logger.getLogger(getClass());

    @Autowired
    RestTemplate restTemplate;

    @HystrixCollapser(batchMethod = "findAll",collapserProperties = {@HystrixProperty(name="timerDelayInMilliseconds",value = "100")})
    public Future<String> find(String id){
        return null;
    }

    @HystrixCommand
    public List<String> findAll(List<String> id){
//        String s= restTemplate.getForObject("http://hello-service/getid/{1}", String.class,StringUtils.join(id,","));
        String[] s= restTemplate.getForObject("http://hello-service/getid?ids={1}", String[].class,StringUtils.join(id,","));

        return Arrays.asList(s);

    }
}

这里通过@HystrixCollapser注解为其创建了合并请求器,通过batchMethod属性指定批量请求的实现方法为findAll方法,同时通过collapserProperties属性为合并请求器设置相关属性,这里使用@HystrixProperty(name="timerDelayInMilliseconds",value = "100")将合并时间窗设置为100毫秒。

请求合并的额外开销

虽然通过请求合并可以减少请求的数量以缓解依赖服务线程池的资源,但是在使用的时候也需要注意它所带来的额外开销:用于请求合并的延迟时间窗会使得依赖服务的请求延迟增高。比如:某个请求在不通过请求合并器访问的平均耗时为5ms,请求合并的延迟时间窗为10ms(默认值),那么当该请求的设置了请求合并器之后,最坏情况下(在延迟时间窗结束时才发起请求)该请求需要15ms才能完成。

由于请求合并器的延迟时间窗会带来额外开销,所以我们是否使用请求合并器需要根据依赖服务调用的实际情况来选择,主要考虑下面两个方面:

  • 请求命令本身的延迟。如果依赖服务的请求命令本身是一个高延迟的命令,那么可以使用请求合并器,因为延迟时间窗的时间消耗就显得莫不足道了。
  • 延迟时间窗内的并发量。如果一个时间窗内只有1-2个请求,那么这样的依赖服务不适合使用请求合并器,这种情况下不但不能提升系统性能,反而会成为系统瓶颈,因为每个请求都需要多消耗一个时间窗才响应。相反,如果一个时间窗内具有很高的并发量,并且服务提供方也实现了批量处理接口,那么使用请求合并器可以有效的减少网络连接数量并极大地提升系统吞吐量,此时延迟时间窗所增加的消耗就可以忽略不计了。

下面贴上完整的代码:首先修改服务提供者:

@RestController
public class HelloController {

    private final Logger logger=Logger.getLogger(getClass());

    @Autowired
    private DiscoveryClient client;

    @RequestMapping("/hello")
    public String index(@RequestParam(defaultValue = "八戒") String id) throws InterruptedException {
//        int sleepTime=new Random().nextInt(3000);
//        logger.info("sleep:"+sleepTime);
//        Thread.sleep(sleepTime);
        logger.info(new Date());
        return "Hello"+new Date()+"---"+new Random().nextInt()+"id:"+id;
    }


    @RequestMapping("/getid")
    public List<String> test(String ids) {
        System.out.println("ids>>>>>>>>>>>>>>>>>>>>>" + ids);
        ArrayList<String> books = new ArrayList<>();
        books.add("AAAAAAAAAAAAAAAAAAA");
        books.add("bbbbbbbbbbbbbbbbbb");
        books.add("cccccccccccccccccc");
        books.add(ids);

        return books;
    }

    @RequestMapping("/getid/{id}")
    public String test2(@PathVariable String id) {
       System.out.println(id);
        return id;
    }

}

然后下面是我的服务调用者代码:

@Service
public class HelloService {

    private final Logger logger=Logger.getLogger(getClass());

    @Autowired
    RestTemplate restTemplate;

    @HystrixCollapser(batchMethod = "findAll",collapserProperties = {@HystrixProperty(name="timerDelayInMilliseconds",value = "100")})
    public Future<String> find(String id){
        return null;
    }

    @HystrixCommand
    public List<String> findAll(List<String> id){
//        String s= restTemplate.getForObject("http://hello-service/getid/{1}", String.class,StringUtils.join(id,","));
        String[] s= restTemplate.getForObject("http://hello-service/getid?ids={1}", String[].class,StringUtils.join(id,","));

        return Arrays.asList(s);

    }
}

我的控制器代码(用于测试):


    @RequestMapping("/finds")
    public String finds(String id) throws ExecutionException, InterruptedException {
        Future<String>s=helloService.find(id);
        Future<String>ss=helloService.find(id);
        Future<String>sss=helloService.find(id);
        String str=s.get();
        String str2=ss.get();
        String str3=sss.get();
        System.out.println(str+"-----"+str2+"-----"+str3);
        Thread.sleep(3000);
        Future<String>sssss=helloService.find(id);
        System.out.println(sssss.get());
        return str;
    }

http://localhost:8099/finds?id=10

Hystrix有4个不同优先级的配置:

1.全局默认值:如果没有设置下面三个属性,那么这个属性就是默认值。由于该属性通过代码定义,所以对于这个级别主要关注它在代码中定义的默认值即可。

2.全局配置属性:通过在配置文件中定义全局属性,在应用启动时或在与Spring Cloud Config和Spring Cloud Bus实现动态刷新配置功能配合下可以实现对“全局默认值”的覆盖以及在运行期对“全局默认值”的动态调整。

3.实例默认值:通过代码为实例定义的默认值。通过代码的方式为实例设置属性值来覆盖默认的全局配置。

4.实例配置属性:通过配置文件来为指定的实例进行属性配置,以覆盖前面的是哪个默认值。他也可以用Spring Cloud Config 和Spring Cloud Bus实现动态刷新配置功能实现对具体实例配置的动态调整。

2.Command属性

command属性主要控制HystrixCommand命令的行为。

它主要有5种不同类型的属性配置。

 

 

  • execution配置

     

     

     

     

     

    • execution.isolation.strategy :该属性用来设置HystrixCommand.run()执行的隔离策略,有如下二个选项:

      • THREAD:通过线程池隔离的策略,在独立线程上执行,并且他的并发限制受线程池中线程数量的限制(默认)
      • SEMAPHONE:通过信号量隔离的策略,在调用线程上执行,并且他的并发限制受信号量计数的限制。
    • execution.isolation.thread.timeoutInMilliseconds:该属性用来配置 HystrixCommand 执行的超时时间,单位为毫秒,默认值 1000 ,超出此时间配置,Hystrix 会将该执行命令为 TIMEOUT 并进入服务降级处理逻辑
    • execution.timeout.enabled:该属性用来配置 HystrixCommand 执行是否启动超时时间,默认值 true,如果设置为 false,则 execution.isolation.thread.timeoutInMilliseconds 属性的配置将不起作用
    • execution.isolation.thread.interruptOnTimeout:该属性用来配置当 HystrixCommand.run()执行超时的时候,是否需要将他中断,默认值 true
    • execution.isolation.semaphore.maxConcurrentRequests:当隔离策略使用信号量时,该属性用来配置信号量的大小(并发请求数),当最大并发请求数达到该设置值,后续的请求将会被拒绝
    •  execution.isolation.thread.interruptOnCancel:该属性用来配置当HystrixCommand.run()执行被取消的时候是否要将它中断。
  • fallback配置

    下面这些属性用来控制HystrixCommand.getFallback()的执行。这些属性同时适用于线程池的信号量的隔离策略。

     

    • fallback.enabled:该属性用来设置服务降级策略是否启用,默认值 true ,如果设置为false,当请求失败或拒绝发生时,将不会调用 HystrixCommand.getFallback() 来执行服务降级逻辑
    • fallback.isolation.semaphore.maxConcurrentRequests:该属性用来设置从调用线程中允许HystrixCommand.getFallback()方法执行的最大并发请求数
  • circuitBreaker 配置

    下面是这些断路器的属性配置,用来控制HystrixCircuitBreaker的行为。

     

     

     

     

    • circuitBreaker.enabled:该属性用来确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求,默认值 true
    • circuitBreaker.requestVolumeThreshold:该属性用来设置在滚动时间窗中,断路器的最小请求数。例如:默认值 20 的情况下,如果滚动时间窗(默认值 10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
    • circuitBreaker.sleepWindowInMilliseconds:该属性用来设置当断路器打开之后的休眠时间窗。默认值 5000 毫秒,休眠时间窗结束之后,会将断路器设置为"半开"状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为"打开"状态,如果成功就设置为"关闭"状态。
    • circuitBreaker.errorThresholdPercentage:该属性用来设置断路器打开的错误百分比条件。例如,默认值为 50 的情况下,表示在滚动时间窗中,在请求数量超过 circuitBreaker.requestVolumeThreshold 阈值的请求下,如果错误请求数的百分比超过50,就把断路器设置为"打开"状态,否则就设置为"关闭"状态。
    • circuitBreaker.forceOpen:该属性如果设为true断路器j将强制进入"打开"状态,会拒绝所有请求,该属性优先于 circuitBreaker.forceClosed
    • circuitBreaker.forceClosed:该属性如果设为true,断路器强制进入"关闭"状态,会接收所有请求。如果circuitBreaker.forceOpens属性为true,该属性不会生效。
  • metrics 配置

    该配置属性与HystrixCommand 和 HystrixObservableCommand 执行中捕获指标信息有关,配置前缀为 hystrix.command.default

     

     

     

    • metrics.rollingStats.timeInMilliseconds:该属性用于设置滚动时间窗的长度,单位毫秒,该时间用于断路器判断健康度时需要收集信息的持续时间,默认值 10000 。断路器值啊收集指标信息时候会根据设置的时间窗长度拆分成多个"桶"来累计各度量值,每个"桶"记录了一段时间内的采集指标。例如当采用默认值10000毫秒时,断路器默认将其拆分成10个桶(桶的数量可以通过metrics.rollingStats.numBuckets参数设置),每个桶记录1000毫秒内的指标信息。
    • metrics.rollingStats.numBuckets:该属性用来设置滚动时间窗统计指标信息时,划分"桶"的数量,默认值 10 。 metrics.rollingStats.timeInMilliseconds 参数的设置必须能被该参数整除,否则将抛出异常。

    • metrics.rollingPercentile.timeInMilliseconds:该属性用来设置百分位统计的滚动窗口的持续时间,单位:毫秒,默认值 60000
    • metrics.rollingPercentile.enabled:该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算,默认值 true ,如果设置为 false 那么所有概要统计都将返回 -1
    • metrics.rollingPercentile.numBuckets:该属性用来设置百分位统计窗口中使用"桶"的数量,默认值 6
    • metrics.rollingPercentile.timeInMilliseconds:该属性用来设置百分位的滚动窗口的持续时间,单位是毫秒。
    • metrics.rollingPercentile.bucketSize:该属性用来设置在执行过程中每个"桶"中保留的最大执行次数,如果在滚动时间窗内发生超该设定值的执行次数,就从最初的位置开始重写,例如:设置为 100,滚动窗口为 10 秒,若在10秒内一个"桶"中发生了500次执行,那么该"桶"中只保留最后的100次执行的统计。另外,增加该值的大小会增加内存量的消耗,并增加排序 百分位数所需的计算时间。默认值 100
    • metrics.healthSnapshot.intervalInMilliseconds:该属性用来设置采集影响断路器状态的健康快照(请求的成功、错误百分比)的间隔等待时间,默认值 500
  • requestContext 配置

    下面这些属性涉及HystrixCommand使用HystrixRequestContext的设置。

     

     

    • requestCache.enabled:该属性用来配置是否开启请求缓存
    • requestLog.enabledg:该属性用来设置 HystrixCommand 的执行和事件是否打印日志到 HystrixRequestLog 中,默认值 true
  • collapser 配置

    该属性除了在代码中用set和配置文件之外,也可以用注解进行配置。可使用@HystrixCollapser中的collapserProperties属性来设置,如:

    @HystrixCollapser(batchMethod="batch",collapserProperties={
        @HystrixProperty(name="timerDelayInMilliseconds",value="20")
    })

     下面这些属性用来控制命令合并相关的行为。

     

     

    • maxRequestsInBatch:该属性用来设置一次请求合并批处理允许的最大请求数量,默认值 Integer.MAX_VALUE
    • timerDelayInMilliseconds:该属性用来设置批处理过程中每个命令延迟的时间,单位毫秒,默认值 10
    • requestCache.enabled:该属性用来设置批处理过程中是否开启请求缓存,默认值 true

     

    threadPool 配置

    该属性除了在代码中用set和配置文件之外,也可以用注解进行配置。可使用@HystrixCollapser中的threadPoolProperties属性来设置,如:

    @HystrixCommand(fallbackMethod="helloFallback",commandKey="helloKey",
        threadPoolProperties={
            @HystrixProperty(name="coreSize",value="20")
        }
    )

     

     

     

    • coreSize:该属性用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量,默认值 10
    • maxQueueSize:该属性用来设置线程池的最大队列大小,当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,否则使用 LinkedBlockingQueue 实现的队列。(该属性只有在初始化时有用,无法通过动态刷新调整)
    • queueSizeRejectionThreshold :该属性用来为队列设置拒绝阈值,即使队列没有到达最大值也能拒绝请求,该属性主要对 LinkedBlockingQueue 队列的补充,因为LinkedBlockingQueue队列不能动态修改它的对象大小。默认值 5,当 maxQueueSize 属性为 -1 时候,该属性无效
    • metrics.rollingPercentile.timeInMilliseconds:该属性用来设置线程池统计的滚动窗口的持续时间,单位:毫秒,默认值 10000。该滚动时间窗的长度用于线程池的指标度量它会被分成多个桶来统计指标。
    • metrics.rollingPercentile.numBuckets:该属性用来设置线程池统计窗口中使用"桶"的数量,默认值 10

参考《Spring Cloud微服务实战》

猜你喜欢

转载自blog.csdn.net/qq_37598011/article/details/82667881