Hystrix学习总结

参考文章

Hystrix的服务熔断和服务降级

服务熔断与降级(Hystrix)

SpringCloud(6)—熔断降级理解、Hystrix实战

白话:服务降级与熔断的区别

Spring Cloud Hystrix服务容错

Spring Cloud Hystrix Dashboard仪表盘

Hystrix原理与实战(文章略长)

Hystrix熔断VS降级

熔断降级是应对雪崩效应的一种微服务链路保户机制,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的相应信息。

相同点

目标一致 都是从可用性和可靠性出发,为了防止系统崩溃;

用户体验类似 最终都让用户体验到的是某些功能暂时不可用;

不同点

降级:访问下游某个服务时,如果发生 超时、失败次数、故障、限流,则访问Hystrix指定的方法,快速返回备用信息,进行降级操作。

熔断:如果访问下游某个服务时,出错次数太多,超过阈值。则直接熔断这个服务调用,下次访问不在调用服务,直接返回备用信息。既 熔断是符合条件的降级。

Hystrix提供了如下的几个关键参数,来对一个熔断器进行配置:

circuitBreaker.requestVolumeThreshold    //滑动窗口的大小,默认为20 
circuitBreaker.sleepWindowInMilliseconds //过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟 
circuitBreaker.errorThresholdPercentage  //错误率,默认50%

3个参数放在一起,所表达的意思就是:

每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。

Hystrix实例

1、引入jar

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

2、入口类上加入 @EnableCircuitBreaker

@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class DemoApplication {
    
    
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
    
    
        return new RestTemplate();
    }
    public static void main(String[] args) {
    
    
        SpringApplication.run(DemoApplication.class, args);
    }
}

加@EnableHystrix也可以。@EnableHystrix和@EnableCircuitBreaker是等价的。

3、@HystrixCommand

@HystrixCommand(fallbackMethod = "getUserDefault")
public User getUser(@PathVariable Long id) {
    
    
   return restTemplate.getForObject("http://Server-Provider/user/{id}", User.class, id);
}

public User getUserDefault(Long id) {
    
    
    User user = new User();
    user.setId(-1L);
    user.setUsername("defaultUser");
    user.setPassword("123456");
    return user;
}

我们在getUser方法上加入了@HystrixCommand注解,注解的fallbackMethod属性指定了被调用的方法不可用时的回调方法(服务熔断时的回调处理逻辑,即服务降级),这里为getUserDefault方法(必须与getUser方法的参数及返回值类型一致)。

Hystrix常用注解

HystrixCommand

属性 说明 样例
fallbackMethod 指定被调用的方法不可用时的回调方法 fallbackMethod = “getUserDefault”
ignoreExceptions 指定某个异常不触发服务降级 ignoreExceptions = {NullPointerException.class}
commandKey 命令的名称 commandKey = “getUserById”
groupKey 组名 groupKey = “userGroup”
threadPoolKey 线程池名称 threadPoolKey = “getUserThread”

对于方法抛出的异常信息,我们可以在服务降级的方法中使用Throwable对象获取

@HystrixCommand(fallbackMethod = "getUserDefault",commandKey = "getUserById", groupKey = "userGroup",
        threadPoolKey = "getUserThread" , ignoreExceptions = {
    
    NullPointerException.class})
public User getUser(@PathVariable Long id) {
    
    
	//代码省略
}

public User getUserDefault(Long id, Throwable e) {
    
    
    //代码省略
}

上面的配置指定了命令的名称为getUserById,组名为userGroup,线程池名称为getUserThread。

通过设置命令组,Hystrix会根据组来组织和统计命令的告警、仪表盘等信息。默认情况下,Hystrix命令通过组名来划分线程池,即组名相同的命令放到同一个线程池里,如果通过threadPoolKey设置了线程池名称,则按照线程池名称划分。

当getUser方法被调用时,日志打印如下:

2018-06-06 15:32:55.945  INFO 16192 --- [getUserThread-1] com.example.demo.Service.UserService  : 获取用户信息

可看到线程名称为getUserThread-1。

Hystrix缓存

1、添加 Filter 。

其实Hystrix的缓存还是蛮鸡肋的,请求缓存不是只写入一次结果就不再变化的,而是每次请求到达Controller的时候,我们都需要为HystrixRequestContext进行初始化,之前的缓存也就是不存在了,我们是在同一个请求中保证结果相同,同一次请求中的第一次访问后对结果进行缓存,缓存的生命周期只有一次请求

@Component
@WebFilter(filterName = "hystrixRequestContextServletFilter", urlPatterns = "/*", asyncSupported = true)
public class HystrixRequestContextServletFilter implements Filter {
    
    
    @Override
    public void init(FilterConfig filterConfig) {
    
    
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        filterChain.doFilter(servletRequest, servletResponse);
        context.close();
    }

    @Override
    public void destroy() {
    
    
    }
}

2、添加 @CacheResult,指定key,指定key有以下方式:

a、通过 @CacheKey 注解指定key

@CacheResult
@HystrixCommand(fallbackMethod = "getUserDefault", commandKey = "getUserById", groupKey = "userGroup",
        threadPoolKey = "getUserThread")
public User getUser(@CacheKey("id") @PathVariable Long id) {
    
    
    log.info("获取用户信息");
    return restTemplate.getForObject("http://Server-Provider/user/{id}", User.class, id);
}

b、通过方法来指定,方法的返回值必须是String类型

public String getCacheKey(Long id) {
    
    
    return String.valueOf(id);
}

@CacheResult(cacheKeyMethod = "getCacheKey")
@HystrixCommand(fallbackMethod = "getUserDefault", commandKey = "getUserById", groupKey = "userGroup",
        threadPoolKey = "getUserThread")
public User getUser(Long id) {
    
    
    log.info("获取用户信息");
    return restTemplate.getForObject("http://Server-Provider/user/{id}", User.class, id);
}

3、@CacheRemove 缓存清除。我们要及时的清除相应的缓存,否则将会导致缓存数据和实际数据不一致的问题。

@CacheRemove(commandKey = "getUserById")
@HystrixCommand
public void updateUser(@CacheKey("id") User user) {
    
    
    this.restTemplate.put("http://Server-Provider/user", user);
}

@CacheRemove的commandKey属性和getUser里定义的一致。

HystrixCollapser 请求合并

请求合并就是将多个单个请求合并成一个请求,去调用服务提供者,从而降低服务提供者负载的,一种应对高并发的解决办法。不常用。想看请点击 Spring Cloud Hystrix服务容错

Hystrix属性

配置方式

execution.isolation.strategy: 该属性用来设置执行的隔离策略,,它有如下两个选项,默认值:THREAD

  1. THREAD: 通过线程池隔离的策略。它在独立的线程上执行, 并且它的并发限制受线程池中线程数量的限制。
  2. SEMAPHORE: 通过信号量隔离的策略。它在调用线程上执行, 并且它的并发限制受信号量计数的限制。

1、全局配置:application.properties

hystrix.command.default.execution.isolation.strategy = "THREAD"

2、实例配置:application.properties

hystrix.command.{HystrixCommandKey}.execution.isolation.strategy = "THREAD"

HystrixCommandKey 是 @HystrixCommand 的 commandKey 值

3,实例配置:@HystrixProperty

    @HystrixCommand(fallbackMethod = "getUserDefault", 
            commandProperties = {
    
    @HystrixProperty(name = "execution.isolation.strategy", value"THREAD")})
    public User getUser(Long id) {
    
    
        log.info("获取用户信息");
        return restTemplate.getForObject("http://Server-Provider/user/{id}", User.class, id);
    }

属性列表

execution 执行配置

execution.isolation.strategy: 该属性用来设置执行的隔离策略,它有如下两个选项。默认值:THREAD

  1. THREAD: 通过线程池隔离的策略。它在独立的线程上执行, 并且它的并发限制受线程池中线程数量的限制。
  2. SEMAPHORE: 通过信号量隔离的策略。它在调用线程上执行, 并且它的并发限制受信号量计数的限制。

execution.isolation.thread.timeoutinMilliseconds: 该属性用来配置HystrixCommand执行的超时时间,单位为毫秒。当HystrixCommand执行时间超过该配置值之后, Hystrix会将该执行命令标记为TIMEOUT并进入服务降级处理逻辑。默认值 : 1000亳秒

execution.timeout.enabled: 该属性用来配置HystrixCommand的执行是否启用超时时间。默认为true, 如果设置为false, 那么execution.isolation.thread.timeoutinMilliseconds属性的配置将不再起作用。

coreSize: 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量。默认:10

execution.isolation.semaphore.maxConcurrentRequests: 当HystrixCommand的隔离策略使用信号量的时候,该属性用来配置信号量的大小(并发请求数)。当最大并发请求数达到该设置值时,后续的请求将会被拒绝。 默认:10

fallback.enabled: 该属性用来设置服务降级策略是否启用,如果设置为false,那么当请求失败或者拒绝发生时,将不会调用降级服务。默认:true

circuitBreaker 熔断器配置

circuitBreaker.enabled: 该属性用来确定当服务请求命令失败时, 是否使用断路器来跟踪其健康指标和熔断请求。默认:true

circuitBreaker.requestVolumeThreshold: 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为20 的时候,如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。默认: 20

circuitBreaker.errorThresholdPercentage: 该属性用来设置断路器打开的错误百分比条件。例如,默认值为5000 的情况下,表示在滚动时间窗中,在请求数量超过circuitBreaker.requestVolumeThreshold阅值的前提下,如果错误请求数的百分比超过50, 就把断路器设置为“打开” 状态, 否则就设置为“关闭” 状态。默认:50

circuitBreaker.sleepWindowinMilliseconds: 该属性用来设置当断路器打开之后的休眠时间窗。休眠时间窗结束之后,会将断路器置为“半开” 状态, 尝试熔断的请求命令,如果依然失败就将断路器继续设置为“打开” 状态,如果成功就设置为“关闭” 状态。默认:5000

circuitBreaker.forceOpen: 如果将该属性设置为true, 断路器将强制进入“打开” 状态,它会拒绝所有请求。该属性优先于circuitBreaker.forceClosed属性。

circuitBreaker.forceClosed: 如果将该属性设置为true, 断路器将强制进入“关闭” 状态, 它会接收所有请求。如果circuitBreaker.forceOpen属性为true, 该属性不会生效。默认:false

requestContext

requestCache.enabled: 此属性用来配置是否开启请求缓存。

其他属性请看 Spring Cloud Hystrix服务容错

Hystrix原理

Hystrix处理流程

在这里插入图片描述

资源隔离

资源隔离主要指对线程的隔离。Hystrix提供了两种线程隔离方式:线程池和信号量。

线程隔离-线程池

Hystrix通过命令模式对发送请求的对象和执行请求的对象进行解耦,将不同类型的业务请求封装为对应的命令请求。

如订单服务查询商品,查询商品请求->商品Command;商品服务查询库存,查询库存请求->库存Command。并且为每个类型的Command配置一个线程池,当第一次创建Command时,根据配置创建一个线程池,并放入ConcurrentHashMap,如商品Command:

final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>();
...
if (!threadPools.containsKey(key)) {
    
    
    threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
}

后续查询商品的请求创建Command时,将会重用已创建的线程池。
在这里插入图片描述
线程隔离-信号量

通过设置信号量来限制对任何给定依赖的并发调用量。

使用线程池时,发送请求的线程和执行依赖服务的线程不是同一个,而使用信号量时,发送请求的线程和执行依赖服务的线程是同一个,都是发起请求的线程。
在这里插入图片描述
线程隔离总结

线程切换 支持异步 支持超时 支持熔断 限流 开销
信号量
线程池

通常情况下,线程池引入的开销足够小,不会有重大的成本或性能影响。但对于一些访问延迟极低的服务,如只依赖内存缓存,线程池引入的开销就比较明显了,这时候使用线程池隔离技术就不适合了,我们需要考虑更轻量级的方式,如信号量隔离。

线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程切换,因此避免了不必要的开销。但是信号量不支持异步,也不支持超时,也就是说当所请求的服务不可用时,信号量会控制超过限制的请求立即返回,但是已经持有信号量的线程只能等待服务响应或从超时中返回,即可能出现长时间等待。线程池模式下,当超过指定时间未响应的服务,Hystrix会通过响应中断的方式通知线程立即结束并返回。

熔断器工作流程

熔断器工作的详细过程如下:

第一步,调用allowRequest()判断是否允许将请求提交到线程池

  1. 如果熔断器强制打开,circuitBreaker.forceOpen为true,不允许放行,返回。
  2. 如果熔断器强制关闭,circuitBreaker.forceClosed为true,允许放行。此外不必关注熔断器实际状态,也就是说熔断器仍然会维护统计数据和开关状态,只是不生效而已。

第二步,调用isOpen()判断熔断器开关是否打开如果熔断器开关打开,进入第三步,否则继续;

  1. 如果一个周期内总的请求数小于circuitBreaker.requestVolumeThreshold的值,允许请求放行,否则继续;
  2. 如果一个周期内错误率小于circuitBreaker.errorThresholdPercentage的值,允许请求放行。否则,打开熔断器开关,进入第三步。

第三步,调用allowSingleTest()判断是否允许单个请求通行,检查依赖服务是否恢复

  1. 如果熔断器打开,且距离熔断器打开的时间或上一次试探请求放行的时间超过circuitBreaker.sleepWindowInMilliseconds的值时,熔断器器进入半开状态,允许放行一个试探请求;否则,不允许放行。

猜你喜欢

转载自blog.csdn.net/fangye1/article/details/111614511