Hystrix是一个用于分布式系统的延迟和容错的开源库。在分布式系统里,许多依赖不可避免的调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。本文主要介绍Hystrix的基本使用,文中使用到的软件版本:Spring Boot 2.2.5.RELEASE、Spring Cloud Hoxton.SR3、Java 1.8.0_191。
1、Hystrix作用
1.1、资源隔离
包括线程池隔离和信号量隔离,限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。
线程切换 | 支持异步 | 支持超时 | 支持熔断 | 限流 | 开销 | |
信号量 | 否 | 否 | 否 | 是 | 是 | 小 |
线程池 | 是 | 是 | 是 | 是 | 是 | 大 |
1.2、降级
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
1.3、融断
当失败率达到阀值自动触发降级(如因网络故障/超时造成的失败率高),熔断器触发的快速失败会进行快速恢复。
1.4、缓存
提供了请求缓存、请求合并实现。
2、Hystrix处理过程
1.构造一个 HystrixCommand或HystrixObservableCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数;
2.执行命令,Hystrix提供了4种执行命令的方法;
3.判断是否使用缓存响应请求,若启用了缓存,且缓存可用,直接使用缓存响应请求。Hystrix支持请求缓存,但需要用户自定义启动;
4.判断熔断器是否打开,如果打开,跳到第8步;
5.判断线程池/队列/信号量是否已满,已满则跳到第8步;
6.执行HystrixObservableCommand.construct()或HystrixCommand.run(),如果执行失败或者超时,跳到第8步;否则,跳到第9步;
7.统计熔断器监控指标;
8.走Fallback备用逻辑
9.返回请求响应
3、Hystrix参数说明
配置项 | 默认值 | 说明 |
---|---|---|
hystrix.command.default.execution.isolation.strategy | THREAD | 隔离策略,THREAD, SEMAPHORE |
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds | 1000 | 线程超时时间 |
hystrix.command.default.execution.timeout.enabled | true | 启用超时设置 |
hystrix.command.default.execution.isolation.thread.interruptOnTimeout | true | 线程超时时,是否可被中断 |
hystrix.command.default.execution.isolation.thread.interruptOnCancel | false | 线程取消时,是否可被中断 |
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests | 10 | 最大并发信号量 |
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests | 10 | 最大并发备用信号量 |
hystrix.command.default.fallback.enabled | true | 启用fallback |
hystrix.command.default.circuitBreaker.enabled | true | 启用断路器 |
hystrix.command.default.circuitBreaker.requestVolumeThreshold | 20 | 在一个时间窗口内触发断路器的最小请求量 |
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds | 5000 | 触发断路器时,服务休眠时间 |
hystrix.command.default.circuitBreaker.errorThresholdPercentage | 50 | 触发断路器时,最小错误比率 |
hystrix.command.default.circuitBreaker.forceOpen | false | 强制打开断路器 |
hystrix.command.default.circuitBreaker.forceClosed | false | 强制关闭断路器 |
hystrix.command.default.metrics.rollingStats.timeInMilliseconds | 10000 | 数据采集时间段 |
hystrix.command.default.metrics.rollingStats.numBuckets | 10 | 采集数据份,必须能被timeInMilliseconds整除 |
hystrix.command.default.metrics.rollingPercentile.enabled | true | 开启采集滚动百分比 |
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds | 60000 | 滚动百分收集时间段 |
hystrix.command.default.metrics.rollingPercentile.numBuckets | 6 | 滚动百分比收集份数,必须能被timeInMilliseconds整除 |
hystrix.command.default.metrics.rollingPercentile.bucketSize | 100 | 每份数据的最大统计请求量 |
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds | 500 | 健康检查间隔时间 |
hystrix.command.default.requestCache.enabled | true | 开启请求缓存,HystrixRequestCache |
hystrix.command.default.requestLog.enabled | true | 开启请求日志,记录在HystrixRequestLog |
hystrix.collapser.default.maxRequestsInBatch | Integer.MAX_VALUE | 单批次最大请求量 |
hystrix.collapser.default.timerDelayInMilliseconds | 10 | 批次请求延迟启动时间 |
hystrix.collapser.default.requestCache.enabled | true | 开启批次请求缓存, HystrixCollapser.execute(), HystrixCollapser.queue() |
hystrix.threadpool.default.coreSize | 10 | 核心线程数 |
hystrix.threadpool.default.maximumSize | 10 | 最大线程数 |
hystrix.threadpool.default.maxQueueSize | -1 | 最大阻塞线程队列 |
hystrix.threadpool.default.queueSizeRejectionThreshold | 5 | 队列上限,超过会拒绝请求 |
hystrix.threadpool.default.keepAliveTimeMinutes | 1 | 线程保持活跃时间(分钟) |
hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize | false | 启用maximumSize参数 |
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds | 10000 | 线程池数据采集时间段 |
hystrix.threadpool.default.metrics.rollingStats.numBuckets | 10 | 线程池数据采集份数 |
4、使用
4.1、结合springboot使用
4.1.1、引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
4.1.2、配置Hystrix(application.yml)
根据需要配置Hystrix,也可以使用默认的配置。参数的意思可以参考上面的参数说明。
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 1000
circuitBreaker:
requestVolumeThreshold: 3
4.1.3、启动类增加@EnableCircuitBreaker(或@EnableHystrix)
package com.inspur.scdemo.client; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableCircuitBreaker public class ClientApplication { public static void main(String[] args) { SpringApplication.run(ClientApplication.class, args); } }
4.1.4、编写controller
package com.inspur.scdemo.client.controller; import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/hystrix") /**defaultFallback为默认的降级方法,没有参数,返回值要与Hystrix方法的返回值相同*/ @DefaultProperties(commandProperties = { @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "3"), @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000"), }, defaultFallback = "defaulFallback") public class HystrixController { private Logger logger = LoggerFactory.getLogger(HystrixController.class); @RequestMapping("/test") /**fallbackMethod为降级方法,需要与Hystrix方法的签名和返回值一致*/ @HystrixCommand( commandProperties = { @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "3"), @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"), }, fallbackMethod = "fallback") public String test(long id) { logger.info(id + ""); try { Thread.sleep(1000 * 5); } catch (Exception e) { e.printStackTrace(); } return "test"; } @RequestMapping("/test2") @HystrixCommand() public String test2(long id) { logger.info(id + ""); try { Thread.sleep(1000 * 5); } catch (Exception e) { e.printStackTrace(); } return "test2"; } public String fallback(long id) { logger.info(id + ""); return "fallback"; } public String defaulFallback() { return "defaulFallback"; } }
方法上的配置优先级最高,其次是类上的配置,最后时配置文件中的配置。
访问http://localhost:9002/hystrix/test?id=2,前三次会等待3秒,然后返回"fallback";第三次已触发熔断,第四次访问会快速返回"fallback"
访问http://localhost:9002/hystrix/test2?id=2,前三次会等待2秒,然后返回"defaultFallback";第三次已触发熔断,第四次会快速返回"defaultFallback"
4.2、与Feign结合使用
4.2.1、配置启用Hystrix
feign:
hystrix:
enabled: true
4.2.2、定义Feign接口
package com.inspur.scdemo.client.feign; import com.inspur.scdemo.client.entity.CallResult; import com.inspur.scdemo.client.entity.User; import com.inspur.scdemo.client.feign.fallback.UserFallBack; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; //scdemo-server为服务名称 @FeignClient(name = "scdemo-server", fallback = UserFallBack.class) public interface IUserFeign { @PostMapping("/user/addUser") CallResult<User> addUser(@RequestBody User user); @PostMapping("/user/getUser") CallResult<User> getUser(@RequestParam("id") long id); }
4.2.3、定义降级实现类
package com.inspur.scdemo.client.feign.fallback; import com.inspur.scdemo.client.entity.CallResult; import com.inspur.scdemo.client.entity.User; import com.inspur.scdemo.client.feign.IUserFeign; import org.springframework.stereotype.Component; @Component public class UserFallBack implements IUserFeign { @Override public CallResult<User> addUser(User user) { return new CallResult<>(1, "增加用户服务暂不可用!"); } @Override public CallResult<User> getUser(long id) { return new CallResult<>(1, "获取用户服务暂不可用!"); } }
4.2.4、Controller中调用Feign接口
package com.inspur.scdemo.client.controller; import com.inspur.scdemo.client.entity.CallResult; import com.inspur.scdemo.client.entity.User; import com.inspur.scdemo.client.feign.IUserFeign; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/userfeign") public class FeignController { private Logger logger = LoggerFactory.getLogger(FeignController.class); @Autowired private IUserFeign userFeign; @RequestMapping("/addUser") public CallResult<User> addUser(String name, String address) { logger.info("name={},address={}", name, address); CallResult<User> result = new CallResult<>(); try { User user = new User(); user.setName(name); user.setAddress(address); CallResult<User> resultServer = userFeign.addUser(user); logger.info("server返回结果:{}" + resultServer); User user2 = resultServer.getResult(); user2.setName("client-" + user2.getName()); result.setResult(user2); } catch (Exception e) { result = new CallResult<User>(-1, "发生异常"); e.printStackTrace(); } return result; } @RequestMapping("/getUser") public CallResult<User> getUser(long id) { logger.info(id + ""); CallResult<User> result = null; try { result = userFeign.getUser(id); } catch (Exception e) { result = new CallResult<User>(-1, "发生异常"); e.printStackTrace(); } return result; } }
当scdemo-server服务不存在或调用超时,都会返回降级实现类中返回的信息。
4.3、与SpringCloud Gateway结合使用
4.3.1、引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
4.3.2、编写回退controller
package com.inspur.scdemo.gateway.controller; import com.inspur.scdemo.gateway.entity.CallResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class FallbackController { @RequestMapping("/fallback") public CallResult<String> fallback() { return new CallResult<>(-10, "服务不可用"); } }
4.3.3、配置Hystrix filter
spring:
cloud:
gateway:
discovery:
locator:
enabled: false #是否开启服务注册和发现功能;将为每一个服务创建一个router,这个router把以服务名开头的请求路径转发到对应的服务上
lower-case-service-id: true #将请求路径上的服务名转为小写
routes:
- id: client
uri: lb://scdemo-client
predicates:
- Path=/scdemo-client/**
filters:
- StripPrefix=1 #路由时会去除/scdemo-client
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
通过网关调用服务时,当scdemo-client服务不存在或调用超时,都会返回降级controller中fallback方法返回的信息。
5、hystrix-dashboard
Hystrix Dashboard是作为断路器状态的一个组件,提供了数据监控和友好的图形化界面,通过Hystrix Dashboard,我们可以在页面上看到服务接口的调用情况。
5.1、dashboard服务(端口9002)
5.1.1、引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
5.1.2、启动类增加@EnableHystrixDashboard
5.1.3、控制台
http://localhost:9002/hystrix/
5.2、被监控的服务(端口9001)
5.2.1、引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
5.2.2、配置端点
management:
endpoints:
web:
exposure:
include: hystrix.stream
5.2.3、查看Hystrix指标流
http://localhost:9001/actuator/hystrix.stream,调用9001上Hystrix保护的接口,会有信息输出:
5.3、dashboard中监控服务
在dashboard的控制台(http://localhost:9002/hystrix/)中输入被监控的服务(http://localhost:9001/actuator/hystrix.stream),点击"Monitor Stream",即可呈现监控的情况:
注:dashboard服务和被监控的服务可以在同一服务里面。