在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用间通过服务注册与订阅的方式相互依赖。但由于每个单元都在不同的进程中运行,一来通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪。
举个例子,在一个电商网站中,我们可能会将系统拆分成用户、订单、库存、积分、评论等一系列服务单元。当用户创建一个订单的时候,客户端将调用订单服务的创建订单接口,此时创建订单接口又会向库存服务来请求出货(判断是否有足够库存来出货)。此时若库存服务因自身处理逻辑等原因造成相应缓慢,会直接导致创建订单服务的线程被挂起,以等待库存申请服务的响应,在漫长的等待之后用户会因为请求库存失败而得到创建订单失败的结果。如果在高并发情况之下,因这些挂起的线程在等待库存服务的响应而未能释放,使得后续到来的创建订单请求被阻塞,最终导致订单服务也不可用。
为了解决服务单元之间的高耦合性,以便于在分布式架构中,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。产生了断路器模式,Spring Cloud Hystrix实现了断路器、线程隔离等一系列服务保护功能。
接下来,我们就从一个简单示例开始对Spring Cloud Hystrix 的学习与使用。
快速入门
在开始使用Spring Cloud Hystrix实现断路器之前,我们先用上一篇实现的一些内容为基础,构建一个如下图架构所示的服务调用关系。
我们在这里需要启动的工程有:
- eureka-server 工程:服务注册中心,端口为1111。
- hello-service工程:HELLO-SERVICE的服务单元,两个实例启动端口分别为8081和8082。
- ribbon-consume工程:使用Ribbon实现的额服务消费者,端口为8091。
在未加入断路器之前,当关闭8081的实例后,发送GET请求到http://localhost:8089/ribbon-consumer,当轮询到8081端口的服务时,得到如下输出:
下面我们开始引入Spring Cloud Hystrix
- 在ribbon-consumer工程的pom.xml的dependency节点中引入spring-cloud-started-hystrix依赖:
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-hystrix</artifactId> 4 </dependency>
- 在ribbon-consumer工程的主类ConsumerApplication中使用@EnableCircuitBreaker注解开启断路器功能:
1 @EnableCircuitBreaker① 2 @EnableDiscoveryClient 3 @SpringBootApplication 4 public class ProvideApplication { 5 6 @Bean 7 @LoadBalanced 8 RestTemplate restTemplate() { 9 return new RestTemplate(); 10 } 11 public static void main(String[] args) { 12 SpringApplication.run(ProvideApplication.class, args); 13 } 14 }
- 改造服务消费方式,新增HelloService类,注入RestTemplate实例。然后,将在ConsumerController中对RestTemplate的使用迁移到helloSerice函数中,最后,在helloService函数上增加@HystrixCommand注解来指定回调方法:
1 @Service 2 public class HelloService { 3 @Autowired 4 RestTemplate restTemplate; 5 6 @HystrixCommand(fallbackMethod = "helloFallback") 7 public String helloService() { 8 return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody(); 9 } 10 11 public String helloFallback() { 12 return "error"; 13 } 14 15 }
- 修改ConsumerController类,注入上面实现的HelloService实例,并在helloConsumer中进行调用:
1 @RestController 2 public class HelloController { 3 4 @Autowired 5 HelloService helloService; 6 7 @RequestMapping(value="/ribbon-consumer",method=RequestMethod.GET) 8 public String helloConsumer(){ 9 return helloService.helloService(); 10 } 11 12 }
下面,我们来验证一下通过断路器实现的服务回调逻辑,重新启动之前关闭的8081端口的HELLO-SERVICE,确保此时服务注册中心、两个HELLO-SERVICE以及RIBBON-CONSUMER均已启动,访问http://localhost:8091/ribbon-consumer可以轮询两个HELLO-SERVICE并返回一些文字信息。此时我们继续断开8081的HELOO-SERVICE,然后访问论寻到8081服务端时,输出内容为error,不再是之前的错误。
至此Hystrix的服务搭建成功!
注释一:可以去掉当前所有注解,使用@SpringCloudApplication注解修饰应用主类