SpringCloud 整合 Hystrix 实现服务容错保护,实战讲解!
一、背景介绍
在上一篇文章中,我们介绍了 Spring Cloud 技术体系中最核心的组件之一 Feign,它主要作用是简化 HTTP 客户端的开发工作,通过简单的几个注解配置即可完成 HTTP 调用的绑定操作。
今天通过这篇文章,结合之前的知识,我们一起来了解一下 Spring Cloud 技术体系中另一个最核心的组件之一 Hystrix。
二、Hystrix 简介
Spring Cloud Hystrix 是一套基于 Netflix Hystrix 实现的断路器工具,主要作用是处理那些可能会失败并且需要被隔离的远程服务调用,以避免导致整个系统崩溃。
在微服务架构中,业务服务之间的调用通常错综复杂,比如某个服务接口 A,当收到用户请求的时候,会调用服务 B 的接口;服务接口 C 可能也会调用服务 B,依次类推。

这种请求链路非常常见,假设服务 B 因为请求量过大而出现无法响应,导致服务不可用。此时所有调用服务 B 的客户端都会出现异常,甚至会像滚雪球一样放大到所有相关的服务。

对于要求高可用的项目来说,显然这是不可接受的。为了解决这个问题,因此产生了断路器这类服务保护的工具。
Hystrix 类似于电路中的断路器,当线路中有点电器发生短路时,此时断路器就会发挥作用,它会及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果,以保护线路的安全。
引入 Hystrix 之后,服务的远程调用逻辑可以用如下图来简要概括。

当调用远程服务失败率达到一定阈值时,Hystrix 会自动打开断路器,阻止对下游服务的进一步调用,从而避免系统资源的浪费和可能的级联失败;当下游服务恢复正常之后,断路器会在一段时间后自动或半自动地关闭,尝试恢复下游服务调用。
下面我们通过具体的例子,看看如何使用 Hystrix 来实现服务自动熔断。
三、方案实践
3.1、Hystrix 使用示例
在此,我们以之前介绍的 Ribbon 为例,依次创建eureka-server
、eureka-provider
工程,就不重复粘贴了。
3.1.1、创建服务消费者
根据eureka-consumer-ribbon
复制一个服务消费者工程,命名为eureka-consumer-ribbon-hystrix
,并在pom.xml
中引入 Hystrix 依赖包,示例如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
3.1.2、开启扫描 Hystrix 功能
然后,创建一个服务启动类并添加@EnableCircuitBreaker
或者@EnableHystrix
注解,表示开启扫描 Hystrix 功能。
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
3.1.3、编写服务降级方法
接着,创建一个远程调用服务RpcService
并使用@HystrixCommand
注解指定服务远程调用失败后的降级方法,示例如下:
@Service
public class RpcService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "fallback")
public String hello() {
String url = "http://eureka-provider/hello";
String result = restTemplate.getForObject(url, String.class);
return result;
}
/**
* 服务降级方法
* @return
*/
public String fallback() {
return "发起远程调用失败,服务自动熔断";
}
}
3.1.4、编写远程调用类
最后,创建一个controller
,通过定义的远程调用服务发起请求。
@RestController
public class HelloController {
@Autowired
private RpcService rpcService;
/**
* 发起远程调用测试
* @return
*/
@GetMapping("/rpc")
public String rpc() {
String result = rpcService.hello();
return "发起远程调用,收到返回的信息:" + result;
}
}
3.1.5、服务测试
完成以上工程之后,依次将eureka-server
、eureka-provider
、eureka-consumer-ribbon-hystrix
服务启动起来。
然后在浏览器上访问eureka-consumer-ribbon-hystrix
服务中的/rpc
接口,中途将eureka-provider
服务停掉,可以得到类似于如下内容。

可以清晰的看到,当客户端远程调用失败后,断路器自动开启并调用指定的服务降级方法。
3.1.6、断路器阈值设置
默认情况下,被 Hystrix 保护的方法,在 10 秒内,调用次数超过了 20 次并且出现 50% 以上的请求发生失败, 那么断路器就会进入打开状态,所有后续的调用都将会由回调方法处理;如果服务提供者挂了,也会直接进入打开状态。在 5 秒之后,断路器进入半开状态,将会再次尝试调用原始的方法,如果请求成功,断路器进入关闭状态。
如果想要调整断路器阈值,例如在 20 秒内,调用次数超过 30 次并且失败率超过 30% 时,断路器自动进入打开状态,可以通过如下方式进行配置。
@HystrixCommand(fallbackMethod = "fallback",commandProperties = {
@HystrixProperty(
name = "circuitBreaker.requestVolumeThreshold",
value = "30"),
@HystrixProperty(
name = "circuitBreaker.errorThresholdPercentage",
value = "30"),
@HystrixProperty(
name = "metrics.rollingStats.timeInMilliseconds",
value = "20000"),
@HystrixProperty(
name = "circuitBreaker.sleepWindowInMilliseconds",
value = "60000")
})
public String hello() {
String url = "http://eureka-provider/hello";
String result = restTemplate.getForObject(url, String.class);
return result;
}
3.2、Feign 使用 Hystrix
在介绍 Feign 使用方式中,我们有提到过 Feign 不仅整合了 Ribbon,还整合了 Hystrix。因此,我们可以在 Feign 客户端中直接使用 Hystrix 相关功能。
根据之前的eureka-consumer-feign
复制一个服务消费者工程,命名为eureka-consumer-feign-hystrix
,相关的依赖包也一起 copy 过来。
3.2.1、添加相关配置
然后,在application.properties
配置文件中,开启 hystrix 熔断。
# 在Feign中开启 hystrix熔断
feign.hystrix.enabled=true
3.2.2、编写服务回调类
接着,创建回调类RpcServiceHystrix
并实现RpcService
接口。
@Component
public class RpcServiceHystrix implements RpcService {
@Override
public String findByName(@RequestParam(value = "name") String name) {
return "请求失败,服务自动降级,请求参数:" +name;
}
}
3.2.3、指定服务回调类
再然后,在@FeignClient
注解中通过fallback
属性指定请求失败时的回调类;同时,在服务提供者中也增加一个/findByName
接口,用于接受请求。
/**
* 配置要调用的服务实例名称,并指定请求失败时的回调类
*/
@FeignClient(name = "eureka-provider", fallback = RpcServiceHystrix.class)
public interface RpcService {
/**
* 要调用的目标服务接口地址
* @return
*/
@RequestMapping(value = "/findByName")
String findByName(@RequestParam(value = "name") String name);
}
3.2.4、编写远程调用类
最后,创建一个controller
,通过定义的 feign 客户端和 hystrix 熔断器来调用服务提供方的接口。
@RestController
public class HelloController {
@Autowired
private RpcService rpcService;
/**
* 发起远程调用测试
* @return
*/
@GetMapping("/rpc")
public String rpc(@RequestParam(value = "name") String name) {
String result = rpcService.findByName(name);
return "通过 feign 发起远程调用(带 hystrix 熔断器),收到返回的信息:" + result;
}
}
3.2.5、服务测试
完成以上工程之后,依次将eureka-server
、eureka-provider
、eureka-consumer-feign-hystrix
服务启动起来。
与之类似,然后在浏览器上访问eureka-consumer-feign-hystrix
服务中的/rpc
接口,中途将eureka-provider
服务停掉,可以得到类似于如下内容。

可以清晰的看到,效果与上文介绍的一样。当客户端远程调用失败后,断路器自动开启并调用指定的服务回调方法。
四、小结
最后总结一下,在微服务架构中,服务之间的依赖关系错综复杂,一个服务的失败可能会影响到调用它的其他服务,进而引发整个系统的崩溃。
Hystrix 就是为了解决这类问题而设计的。Hystrix 具备断路器模式、服务降级、请求缓存、请求合并、线程隔离、自动回退等强大功能,由 Netflix 开发并开源。
尽管之后 Netflix 宣布对其停止维护,但是 Hystrix 仍然是一个值得学习和了解的重要工具,特别是在理解分布式系统容错和断路器模式方面。
五、参考
1.http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html
2.https://www.didispace.com/spring-cloud/springcloud3.html#netflix-hystrix
作者:潘志的技术笔记
出处:https://pzblog.cn/
版权归作者所有,转载请注明出处
