在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元间通过服务注册与订阅的方式互相依赖。由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会出现因等待出现故障的依赖方响应而形成任务积压,最终导致自身服务的瘫痪。
举个例子,在一个电商网站中,我们可能会将系统拆分成,用户、订单、库存、积分、评论等一系列的服务单元。用户创建一个订单的时候,在调用订单服务创建订单的时候,会向库存服务来请求出货(判断是否有足够库存来出货)。此时若库存服务因网络原因无法被访问到,导致创建订单服务的线程进入等待库存申请服务的响应,在漫长的等待之后用户会因为请求库存失败而得到创建订单失败的结果。如果在高并发情况之下,因这些等待线程在等待库存服务的响应而未能释放,使得后续到来的创建订单请求被阻塞,最终导致订单服务也不可用。
在微服务架构中,存在着那么多的服务单元,若一个单元出现故障,就会因依赖关系形成故障蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构就更加的不稳定。为了解决这样的问题,因此产生了断路器模式。
什么是断路器
断路器模式源于Martin Fowler的Circuit Breaker一文。“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
Netflix Hystrix
在Spring Cloud中使用了Hystrix 来实现断路器的功能。Hystrix是Netflix开源的微服务框架套件之一,该框架目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。
下面我们来看看如何使用Hystrix。
准备工作
在开始加入断路器之前,我们先拿之前构建两个微服务为基础进行下面的操作,主要使用下面几个工程:
服务注册中心:端口8761
服务提供者:端口8763
服务消费者-Ribbon:端口8762
服务消费者-Feign:端口8662
Ribbon中引入Hystrix
依次启动服务注册中心、服务提供者(service-hi)、服务消费者-Ribbon工程
访问http://localhost:8762/add可以看到浏览器显示30
关闭服务提供者(service-hi)服务
访问:http://localhost:8762/add,获得以下报错信息
引入hystrix断路器
在ribbon项目的pom文件中引入如下依赖:
<!-- 引入hystrix断路器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
新建service,内容如下:
package com.shijie.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* Created by Administrator on 2017/8/25 0025.
*/
@Service
public class ConsumerService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "addServiceFallback")
public String addService() {
return restTemplate.getForEntity("http://service-hi/add?a=10&b=20", String.class).getBody();
}
public String addServiceFallback() {
return "error";
}
}
在主应用文件加入@EnableCircuitBreaker注解,修改controller文件:
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ServiceRibbonApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ServiceRibbonApplication.class, args);
}
}
重新启动以下项目:
服务注册中心:端口8761
服务提供者:端口8763
服务消费者-Ribbon:端口8762
浏览器访问:htp://localhost:8762/add,浏览器打印30
关闭服务提供者,再次访问以上路径,可以看到浏览器打印error
Feign使用Hystrix
Feign中已经依赖了Hystrix
新建fallback包,新建一个类,实现ServiceHiClient接口,具体代码如下;
package com.shijie.fallback;
import com.shijie.servicehi.ServiceHiClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Created by shijie on 2017/8/25 0025.
*/
@Component
public class ServiceHiClientHystrix implements ServiceHiClient {
@Override
public Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b) {
return -4;
}
}
修改ServiceHiClient.java,添加fallback指向ServiceHiClientHystrix类,内容如下:
package com.shijie.servicehi;
import com.shijie.fallback.ServiceHiClientHystrix;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Created by shijie on 2017/8/24 0024.
*/
@FeignClient(value = "service-hi", fallback = ServiceHiClientHystrix.class)
public interface ServiceHiClient {
@RequestMapping(method = RequestMethod.GET, value = "/add")
Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b);
}
重要的一步,修改applicaton.yml文件,添加feign配置,打开断路器,内容如下:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8662
spring:
application:
name: feign-consumer
feign:
hystrix:
enabled: true
重复之前的操作,将service-hi服务提供方关闭,可以看到和ribbon一样的效果