1.背景
在微服务架构中,服务间根据业务拆分成多个,并且服务之间相互调用。
为了保证高可用,服务通常会集群部署,但是由于网络或自身原因,服务并不能保证100%可用。若某个服务出现问题,调用就会出现阻塞。若Servlet容器线程资源会被消耗完毕,会导致服务瘫痪。
为了解决以上问题引入了断路器(Hystrix),hystrix具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。起到了微服务的保护机制,防止某个单元出现故障.从而引起依赖关系引发故障的蔓延,最终导致整个系统的瘫痪。
简言之断路器(Hystrix)主要作用是,当服务不可用时,不是报错,而是返回自定义的默认值,SpringCloud调用服务有两种方式分别是Ribbon与Feign,本文主将介绍,两种调用方式下如何实现断路器(Hystrix)
2.Ribbon
2.1添加依赖
在server-consum-ribbon 项目添加依赖 spring-cloud-starter-netflix-hystrix
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.2启动类开启开关
在程序的启动类 加 @EnableHystrix 注解开启Hystrix
/*** Ribbon 方式 服务消费者-实例** @author 程序员小强*/
@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonApplication.class, args);}/*** 初始化 Rest模板 bean** @LoadBalanced 注解表明这个restRemplate开启负载均衡的功能*/@Bean@LoadBalancedRestTemplate restTemplate() {
return new RestTemplate();}}
2.3定义断路方法
在HelloServiceApi 示例hello方法中添加一个断路后执行的方法。 hystrixError并通过,注解@HystrixCommand(fallbackMethod = “hystrixError”)指定方法名。
@Service
public class HelloServiceApi {
@Autowiredprivate RestTemplate restTemplate;@HystrixCommand(fallbackMethod = "hystrixError")public Map<String, Object> hello(String consumerApplicationName) {
return restTemplate.getForObject("http://server-provider/hello?consumerApplicationName=" + consumerApplicationName, Map.class);}public Map<String, Object> hystrixError(String consumerApplicationName) {
Map<String, Object> resultMap = new LinkedHashMap<>();resultMap.put("code", 500);resultMap.put("messages", "网络繁忙,请稍后再试");resultMap.put("consumerApplicationName", consumerApplicationName);return resultMap;}
}
2.4断路测试
现有注册中心中,有server-consum-ribbon服务调用server-provider服务
当服务正常时访问结果
当停止生产者server-provider服务再访问时,可以看到触发了断路器,返回了自定义的默认值。
3.Feign
3.1添加依赖
在server-consum-feign 项目由于已经添加了feign依赖,feign已经默认集成了Hystrix。所以不用再添加多余依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.2配置开启开关
# hystrix 支持
feign:hystrix:enabled: true
3.3定义断路实现Api
/*** Hystric 组件** @author 程序员小强*/
@Component
public class HelloFeignServiceHystric implements HelloFeignServiceApi {
/*** 断路 hello方法** @param consumerApplicationName*/@Overridepublic Map<String, Object> hello(String consumerApplicationName) {
Map<String, Object> resultMap = new LinkedHashMap<>();resultMap.put("code", 500);resultMap.put("messages", "网络繁忙,请稍后再试");resultMap.put("consumerApplicationName", consumerApplicationName);return resultMap;}}
3.4使用断路实现
在@FeignClient注解fallback属性指定断路实现
/*** 调用生产者测试接口** @author 程序员小强*/
@FeignClient(value = "server-provider", fallback = HelloFeignServiceHystric.class)
public interface HelloFeignServiceApi {
@RequestMapping(value = "/hello", method = RequestMethod.GET)Map<String, Object> hello(@RequestParam(value = "consumerApplicationName") String consumerApplicationName);
}
3.5断路测试
现有注册中心中,有server-consum-feign服务调用server-provider服务
当服务正常时访问结果
当停止生产者server-provider服务再访问时,可以看到触发了断路器,返回了自定义的默认值。
3.6断路异常信息
问题1:添加了如下配置,但是
@FeignClient(value = "server-provider", fallback = HelloFeignServiceHystric.class)
当调用生产者服务,服务不存在下,会返回断路配置的默认值。
但是异常信息去哪里了呢?异常信息对我们排查问题尤为重要。
3.6.1FallbackFactory断路实现
换fallbackFactory实现方式,( 注意:与fallback方式不能同时使用 )
修改@FeignClient中fallback属性,改为fallbackFactory。
@FeignClient(value = “server-provider”, fallbackFactory = HelloFeignServiceHystricFactory.class)
定义FallbackFactory实现类
/*** Feign -FallbackFactory 断路实现** @author 程序员小强*/
@Component
public class HelloFeignServiceHystricFactory implements FallbackFactory<HelloFeignServiceApi> {
private static final Logger log = LoggerFactory.getLogger(HelloFeignServiceHystricFactory.class);@Overridepublic HelloFeignServiceApi create(Throwable throwable) {
String message = throwable.getMessage();log.error("[ 服务 hystrix 触发了 ] message:{} ", message, throwable);return new HelloFeignServiceApi() {
@Overridepublic Map<String, Object> hello(String consumerApplicationName) {
Map<String, Object> resultMap = new LinkedHashMap<>();resultMap.put("code", 500);resultMap.put("messages", "网络繁忙,请稍后再试");resultMap.put("consumerApplicationName", consumerApplicationName);return resultMap;}};}
}
日志这样就可以打印了
关注程序员小强公众号更多编程趣事,知识心得与您分享