在一个具有多服务的应用中, 假如由于其中某一个服务出现问题, 导致响应速度变慢, 或是根本没有响应返回, 会导致它的服务消费者由于长时间的等待, 消耗尽线程, 进而影响到对其他服务的线程调用, 进而会转变为整个应用的故障. 这也被称之为雪崩效应.
而 Hystrix 熔断器, 正是用来帮助我们解决这种问题的工具.
Hystrix 提供了熔断, 隔离, fallback,cache, 监控等功能, 能够在一个或多个服务出现问题的时候保证整个应用依然处于可用状态.
没有做熔断处理的应用, 出现问题后:
正常情况: A→B→C→D
D 服务出现了问题: A→B→C*D
从而导致 C 服务无法获取正确的响应, 出现对应的问题: A→B*C*D
最后导致 B 服务, 甚至 A 服务都同样出现问题, 由服务不可用变为应用不可用: A*B*C*D
针对于上面的这种情况, 在 Hystrix 中采用了如下的几种方式进行处理.
1.Hystrix 请求超时
用 hystrix 监控请求的响应时间, 如果超过我们设置的时间, 将会被判定为请求超时, 抛出 TimeoutException.
(1) 引入 Maven 依赖
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-hystrix</artifactId>
- <version>1.2.7.RELEASE</version>
- </dependency>
(2) 在 properties 中进行配置.
关于参数详解可以参考,,
- # 超时时间, 默认 1000, 单位 ms.
- # 注意这里配置的 default, 默认所有的请求服务都被设定为这个值. 如果要针对某一个服务, 可以将 default 改为服务名如 user, 或在请求类中通过注解进行单独配置
- hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
(3) 在启动类中添加注解 @EnableCircuitBreaker
- @SpringBootApplication
- @EnableEurekaClient
- @EnableCircuitBreaker
- public class RoleServerApplication {
- public static void main(String[] args) {
- SpringApplication.run(RoleServerApplication.class, args);
- }
- }
(4) 在需要被 Hystrix 控制请求时间的方法上添加注解 @HystrixCommand. 如果方法没有被配置这个注解, 请求将不会达到指定时间后抛出超时异常.
- @GetMapping("roles/{id}")
- @HystrixCommand
- public String getRole(@PathVariable("id") String id) {
- System.out.println("接收到请求 [/roles/" + id + "]");
- return restTemplate.getForObject(rest_url_prefix + "/users/" + id, String.class);
- }
如此完成了一个请求的超时配置, 假如方法 getRole() 对 / users/{id} 请求的时间超过了一秒没有得到相应, 那么会抛出如下异常:
2. 对服务分别配置线程池调用
不同的服务配置了不同的线程池, 这样可以即使向服务 A 发送的请求都超时, 占满了线程数. 但是向服务 B 发送的请求仍然处于正常状态, 不会受到 A 服务的干扰.
(1) 引入 MAVEN 依赖
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-hystrix</artifactId>
- <version>1.2.7.RELEASE</version>
- </dependency>
(2) 在 properties 中进行配置 (注意这里配置的都是 default, 默认适应所有的被 @HystrixCommand 修饰的方法中的请求, 如果需要分别针对不同的服务, 替换 defalut 即可).
- # 超时时间, 默认 1000, 单位 ms.
- hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=100000
- # 线程池核心线程数, 最多同时只有 2 个线程在执行
- hystrix.threadpool.default.coreSize=2
- # 线程池最大队列数, 也可以理解为最多只能添加 4 个请求进来. 包括正在执行的请求.
- hystrix.threadpool.default.maxQueueSize=4
- # 线程池最大线程数. 最多只能接收 2+3=5 个线程数.
- hystrix.threadpool.default.maximumSize=3
- # 队列拒绝阈值, 即使队列数没有达到 maxQueueSize, 也会拒绝接收任务
- hystrix.threadpool.default.queueSizeRejectionThreshold=6
(3) 在启动类中添加注解 @EnableCircuitBreaker
- @SpringBootApplication
- @EnableEurekaClient
- @EnableCircuitBreaker
- public class RoleServerApplication {
- public static void main(String[] args) {
- SpringApplication.run(RoleServerApplication.class, args);
- }
- }
(4) 在需要被 Hystrix 控制请求时间的方法上添加注解 @HystrixCommand, 同时在注解内填入应急方法的方法名, 并定义好应急方法.
- @GetMapping("roles/{id}")
- @HystrixCommand(fallbackMethod = "getRoleFallbackMethod")
- public String getRole(@PathVariable("id") String id) {
- System.out.println(LocalDateTime.now().toString() + "接收到请求 [/roles/" + id + "]");
- return restTemplate.getForObject(rest_url_prefix + "/users/" + id, String.class);
- }
- public String getRoleFallbackMethod(String id) {
- System.out.println(LocalDateTime.now().toString() + "进入 fallBackMethod!");
- return "进入 fallBackMethod";
- }
上面的步骤执行完成后, 我们再多次调用 getRole(String) 方法, 根据我们上面配置的参数, 设置超时时间 100 秒是为了让请求一直保持存活状态.
假如请求的线程数超过了线程池中允许的最大线程, 会抛出线程池拒绝异常, 或是进入 fallback 方法.
3.Hystrix 服务熔断
不论是请求超时还是线程池的配置, 服务熔断做的更为彻底.
在一定的时间范围内, 向 A 服务发送的请求失败率达到了一个指定的比例, 会开启熔断器. 熔断器开启后, 所有向 A 服务发起的请求都将不会被发起请求, 而是或抛出异常, 或调用 fallback 方法. 当熔断器开启达到指定时间后, 会重新向 A 服务发起一次请求, 如果请求失败, 熔断器继续保持开启状态. 如果请求成功, 则熔断器关闭.
(1) 引入 MAVEN 依赖
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-hystrix</artifactId>
- <version>1.2.7.RELEASE</version>
- </dependency>
(2) 在 properties 中进行配置 (注意这里配置的都是 default, 默认适应所有的被 @HystrixCommand 修饰的方法中的请求).
- # 一个 rolling Windows 内最小的请求数. 如果请求数少于该值, 则不论如何也不会触发短路.
- hystrix.command.default.circuitBreaker.requestVolumeThreshold=3
- # 错误率阈值. 如果一个 rolling Windows 内的请求错误率超过了该值, 则会开启熔断器
- hystrix.command.default.circuitBreaker.errorThresholdPercentage=0.5
- # 触发短路的时间值, 单位毫秒.
- hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
(3) 在启动类中添加注解 @EnableCircuitBreaker
- @SpringBootApplication
- @EnableEurekaClient
- @EnableCircuitBreaker
- public class RoleServerApplication {
- public static void main(String[] args) {
- SpringApplication.run(RoleServerApplication.class, args);
- }
- }
(4) 在需要被 Hystrix 控制请求时间的方法上添加注解 @HystrixCommand, 同时在注解内填入应急方法的方法名, 并定义好应急方法.
- @GetMapping("roles/{id}")
- @HystrixCommand(fallbackMethod = "getRoleFallbackMethod")
- public String getRole(@PathVariable("id") String id) {
- System.out.println(LocalDateTime.now().toString() + "接收到请求 [/roles/" + id + "]");
- return restTemplate.getForObject(rest_url_prefix + "/users/" + id, String.class);
- }
- public String getRoleFallbackMethod(String id) {
- System.out.println(LocalDateTime.now().toString() + "进入 fallBackMethod!");
- return "进入 fallBackMethod";
- }
(5) 这里我们编写一个服务提供者代码, 假如接收到的 id 中有 1, 那么会正确返回, 如果接收到的 id 中没有 1, 那么会休眠 100 秒, 导致请求超时.
- @GetMapping("users/{id}")
- public String getUser(@PathVariable("id") String id) {
- if (StringUtils.contains(id, "1")) {
- return "ok";
- }
- try {
- Thread.sleep(100*1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- String user = service.findUser(id);
- return user;
- }
请求结果:
我们可以先测试 id=1 的请求, 如下图, 发现请求正常, 此时 circuit 处于 closed 状态.
然后我们再发送几条 id 为其他值的请求, 此时发现请求 error,circuit 已经变成了 open 状态. 这个时候即使我们发送 id=1 的请求也同样会被进入到 fallback 方法中.
只有当 circuit 被开启了指定的 sleepWindowInMillisecond 数后, 再发送 id=1 的数据, circuit 会重新变为 close.
来源: http://www.bubuko.com/infodetail-3067835.html