接上篇《​​17.Hystrix简介及简单代码示例​​》  Spring Cloud版本为Finchley.SR2版

上一篇我们简单介绍了Hystrix的基本个概念,并编写了一个实例来实现一个基于Hystrix的断路器服务。本篇我们来学习Hystrix的配置上的相关知识。
本部分官方文档:​​​https://cloud.spring.io/spring-cloud-static/Finchley.SR2/single/spring-cloud.html#_circuit_breaker_hystrix_clients​

一、Command Properties

上一篇的最后我们提到,要配置@HystrixCommand,可以在@HystrixProperty注解列表配置commandProperties属性。
有关于@HystrixCommand的注解参数、commandProperties属性,自然要查看javanica的文档:
​​​https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica#configuration​​​ 根据javanica文档的指导,我们可以了解以下信息:
在@HystrixCommand中我们可以使用“commandProperties”指定命令参数,例如:

@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
})
public User getUserById(String id) {
return userResource.getUserById(id);
}

可以看到,这里指定了“降级处理超时时间”为500毫秒。我们在之前对于降级处理时间的配置,都是在全局配置文件application.yml中配置的,commandProperties可以让我们在一些具有独特要求的服务上,单独进行一些配置操作。
commandProperties能够配置的参数如下(参考原文​​​https://github.com/Netflix/Hystrix/wiki/Configuration#ThreadPool​​):

Command属性主要用来控制HystrixCommand命令的行为,它主要分下面的类别:

1、Execution:用来控制HystrixCommand.run()的执行
execution.isolation.strategy:该属性用来设置HystrixCommand.run()执行的隔离策略。默认为THREAD。
execution.isolation.thread.timeoutInMilliseconds:该属性用来配置HystrixCommand执行的超时时间,单位为毫秒。
execution.timeout.enabled:该属性用来配置HystrixCommand.run()的执行是否启用超时时间。默认为true。
execution.isolation.thread.interruptOnTimeout:该属性用来配置当HystrixCommand.run()执行超时的时候是否要它中断。
execution.isolation.thread.interruptOnCancel:该属性用来配置当HystrixCommand.run()执行取消时是否要它中断。
execution.isolation.semaphore.maxConcurrentRequests:当HystrixCommand命令的隔离策略使用信号量时,该属性用来配置信号量的大小。当最大并发请求达到该设置值时,后续的请求将被拒绝。

2、Fallback:用来控制HystrixCommand.getFallback()的执行
fallback.isolation.semaphore.maxConcurrentRequests:该属性用来设置从调用线程中允许HystrixCommand.getFallback()方法执行的最大并发请求数。当达到最大并发请求时,后续的请求将会被拒绝并抛出异常。
fallback.enabled:该属性用来设置服务降级策略是否启用,默认是true。如果设置为false,当请求失败或者拒绝发生时,将不会调用HystrixCommand.getFallback()来执行服务降级逻辑。

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

4、Metrics:该属性与HystrixCommand和HystrixObservableCommand执行中捕获的指标相关。
metrics.rollingStats.timeInMilliseconds:该属性用来设置滚动时间窗的长度,单位为毫秒。该时间用于断路器判断健康度时需要收集信息的持续时间。断路器在收集指标信息时会根据设置的时间窗长度拆分成多个桶来累计各度量值,每个桶记录了一段时间的采集指标。例如,当为默认值10000毫秒时,断路器默认将其分成10个桶,每个桶记录1000毫秒内的指标信息。
metrics.rollingStats.numBuckets:用来设置滚动时间窗统计指标信息时划分“桶”的数量。默认值为10。
metrics.rollingPercentile.enabled:用来设置对命令执行延迟是否使用百分位数来跟踪和计算。默认为true,如果设置为false,那么所有的概要统计都将返回-1。
metrics.rollingPercentile.timeInMilliseconds:用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
metrics.rollingPercentile.numBuckets:用来设置百分位统计滚动窗口中使用桶的数量。
metrics.rollingPercentile.bucketSize:用来设置每个“桶”中保留的最大执行数。
metrics.healthSnapshot.intervalInMilliseconds:用来设置采集影响断路器状态的健康快照的间隔等待时间。

5、Request Context:涉及HystrixCommand使用HystrixRequestContext的设置。
requestCache.enabled:用来配置是否开启请求缓存。
requestLog.enabled:用来设置HystrixCommand的执行和事件是否打印到日志的HystrixRequestLog中。

可以看到,这些参数主要是围绕Hystrix的run执行、getFallback获取降级服务、HystrixCircuitBreaker行为、捕获指标的统计设置这几项来的,这里我们不会一一试验这些参数,但是会挑重要的参数进行讲解,例如下面的隔离策略。

二、Hystrix线程隔离策略与传播上下文

要知道,我们请求@HystrixCommand注释修饰的一个服务时,HystrixCommand的运行逻辑有可能是在该请求的主线程上一并执行,也有可能是单独起一个线程来执行,这取决于我们如何设置Hystrix线程的隔离策略。
execution.isolation.strategy属性就是用来设置HystrixCommand.run()执行的隔离策略的,有两种隔离策略,分别是线程隔离和信号量隔离,即“THREAD”和“SEMAPHORE”,系统默认为“THREAD”。
它们的含义是:
THREAD(线程隔离):使用该方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中线程数量的限制。
SEMAPHORE(信号量隔离):使用该方式,HystrixCommand将会在调用线程上执行,开销相对较小,并发请求受到信号量个数的限制。 
Hystrix中默认并且推荐使用线程隔离(THREAD),因为这种方式有一个除网络超时以外的额外保护。 
一般来说,只有当调用负载异常高时(例如每个实例每秒调用数百次)才需要信号量隔离,因为这种场景下使用THREAD开销会比较高。信号量隔离一般仅适用于非网络调用的隔离。 
正常情况下,默认为线程隔离, 保持默认即可。 
如果发生找不到上下文运行时异常,则需要使用相同的线程(因为请求是一个线程,@HystrixCommand是另一个隔离的线程),可考虑将隔离策略设置为SEMAPHORE。
例如我们为之前findUserById方法添加commandProperties参数:

@GetMapping("/movie/{id}")
@HystrixCommand(fallbackMethod = "findUserByIdFallback",
commandProperties = {@HystrixProperty(name="execution.isolation.strategy",value="SEMAPHORE")})
public User findUserById(@PathVariable Long id){
return userFeignClient.findById(id);
}

public User findUserByIdFallback(Long id){
User user = new User();
user.setId(0L);
return user;
}

我们如果使用@SessionScope或@RequestScope注解,也能达到同样的效果。
我们来回忆一下,Spring之前定义bean的生命周期的时候,会定义一个scope参数:

<bean id ="mockObject1" class="..."   scope="prototype" />

这里的scope用来声明IOC容器中的对象应该处的限定场景或者说该对象的存活空间,即在IOC容器在对象进入相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。
目前,scope的取值有5种。 
在Spring 2.0之前,有singleton和prototype两种;
在Spring 2.0之后,为支持web应用的ApplicationContext,推出另外三种:request,session和global session类型。
他们的含义为:
(1)singleton:为“单一实例”的意思,一个容器中只存在一个实例,所有对该类型bean的依赖都引用这一单一实例。
(2)prototype:容器在接受到该类型的对象的请求的时候,会每次都重新生成一个新的对象给请求方。
(3)request:XmlWebApplicationContext会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,该对象的生命周期即告结束。当同时有10个HTTP请求进来的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,从不是很严格的意义上说,request可以看做prototype的一种特例。
(4)session:Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,他比request scope的bean会存活更长的时间。
(5)global session:global session只有应用在基于porlet的web应用程序中才有意义,他映射到porlet的global范围的session,如果普通的servlet的web应用中使用了这个scope,容器会把它作为普通的session的scope对待。除了在配置文件中配置scope,我们可以在相关bean类上使用@scope注解来进行生命周期的设定,例如“@scope("singleton")”。这里的@SessionScope或@RequestScope注解是对原有的Scope进行扩展的注解,用来指定request和session的域和默认的代理模式。
所以上面的例子还可以写为:

@RestController
@SessionScope
@Scope("session")
public class MovieController {

@Autowired
private RestTemplate restRemplate;

@Autowired
private UserFeignClient userFeignClient;

@GetMapping("/movie/{id}")
@HystrixCommand(fallbackMethod = "findUserByIdFallback")
public User findUserById(@PathVariable Long id){
return userFeignClient.findById(id);
}

public User findUserByIdFallback(Long id){
User user = new User();
user.setId(0L);
return user;
}
}

总结:
● Hystrix的隔离策略有THREAD和SEMAPHORE两种,默认是THREAD。
● 正常情况下,保持默认即可。
● 如果发生找不到上下文的运行时异常,可考虑将隔离策略设置为SEMAPHORE。

下一篇我们继续讲解Hystrix的健康监控以及数据流

参考:《51CTO学院Spring Cloud高级视频》