在微服务架构中,存在着许许多多的服务单元,若一个服务出现故障,就会因依赖关系形成故障蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构就更加的不稳定。为了解决这样的问题,因此产生了断路器模式。
什么是断路器
断路器模式源于Martin Fowler的Circuit Breaker一文。“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
Netflix Hystrix
在Spring Cloud中使用了Hystrix 来实现断路器的功能。Hystrix是Netflix开源的微服务框架套件之一,该框架目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。
准备工作
在开始加入断路器之前,我们先拿之前构建两个微服务为基础进行下面的操作,主要使用下面几个工程:
eureka-servereureka-server工程:服务注册中心,端口1111
person-service工程:person-service服务,端口2222
eureka-ribbon
eureka-ribbon:通过ribbon实现的服务单元,依赖person-service的服务,端口4444
eureka-feign
eureka-feign:通过feign实现的服务单元,依赖person-service的服务,端口5555
Ribbon中引入Hystrix
依次启动eureka-server、person-service、eureka-ribbon工程
访问http://localhost:1111/可以看到注册中心的状态
访问http://localhost:4444/person,调用eureka-ribbon的服务,该服务会去调用person-service的服务,页面显示结果。
关闭person-service服务,访问http://localhost:4444/person,我们获得了下面的报错信息:
pom.xml中引入依赖hystrix依赖
org.springframework.cloud
spring-cloud-starter-hystrix
在eureka-ribbon的主类RibbonApplication中使用@EnableCircuitBreaker注解开启断路器功能:
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class EurekaRibbonApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(EurekaRibbonApplication.class, args);
}
}
改造服务消费方式,增加PersonService类,在使用ribbon消费服务的函数上增加@HystrixCommand注解来指定回调方法。
@Service
public class PersonService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "personServiceFallback")
public String personService() {
return restTemplate.getForEntity("http://PERSON-SERVICE/person?firstname=William&lastname=Sun", String.class).getBody();
}
public String personServiceFallback() {
return "error";
}
}
提供rest接口的Controller改为调用PersonService的personService
@RestController
public class ConsumerController {
@Autowired
private PersonService personService;
@RequestMapping(value = "/person", method = RequestMethod.GET)
public String person() {
return personService.personService();
}
}
然后验证断路器的回调
依次启动eureka-server、person-service、eureka-ribbon工程
访问http://localhost:1111/可以看到注册中心的状态
访问http://localhost:4444/person,页面显示正常。
关闭person-service服务后再访问http://localhost:4444/person,页面显示:
Feign使用Hystrix
不需要在Feigh工程中引入Hystix,Feign中已经依赖了Hystrix.
application.properties,做以下修改:
spring.application.name=feign-consumer
server.port=5555
feign.hystrix.enabled=true
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
增加了feign.hystrix.enabled=true
然后尝试下面你的操作:
依次启动eureka-server、person-service、eureka-feign工程
访问http://localhost:1111/可以看到注册中心的状态
访问http://localhost:5555/person,调用eureka-feign的服务,该服务会去调用person-service的服务,页面显示正常结果。
关闭person-service服务,访问http://localhost:5555/person,我们获得了下面的报错信息:
使用@FeignClient注解中的fallback属性指定回调类
@FeignClient(value = "person-service", fallback = PersonClientHystrix.class)
public interface PersonClient {
@RequestMapping(method = RequestMethod.GET, value = "/person")
String person(@RequestParam(value = "firstname") String firstname, @RequestParam(value = "lastname") String lastname);
}
创建回调类PersonClientHystrix,实现@FeignClient的接口,此时实现的方法就是对应@FeignClient接口中映射的fallback函数。
@Component
public class PersonClientHystrix implements PersonClient {
@Override
public String person( @RequestParam(value = "firstname") String firstname, @RequestParam(value = "lastname") String lastname) {
// TODO Auto-generated method stub
return "Error!";
}
}
再次验证一下,在person-service服务不可用的情况下,页面返回:
工程可参见:hystrix