雪崩效应
在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因 “服务提供者” 的不可用导致 “服务消费者” 的不可用, 并将不可用逐渐放大的过程。
如果下图所示:A 作为服务提供者,B 为 A 的服务消费者,C 和 D 是 B 的服务消费者。A 不可用引起了 B 的不可用,并将不可用像滚雪球一样放大到 C 和 D 时,雪崩效应就形成了。
断路器
Netflix 创建了一个名为 Hystrix 的库, 实现了断路器的模式。“断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
当然,在请求失败频率较低的情况下,Hystrix 还是会直接把故障返回给客户端。只有当失败次数达到阈值时,断路器打开并且不进行后续通信,而是直接返回备选响应。当然,Hystrix 的备选响应也是可以由开发者定制的。
Ribbon 整合 Hystrix
新建spring start project,导入依赖
1 <dependencies> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-web</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>org.springframework.cloud</groupId> 8 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 9 </dependency> 10 <dependency> 11 <groupId>org.springframework.cloud</groupId> 12 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 13 </dependency> 14 <dependency> 15 <groupId>org.springframework.cloud</groupId> 16 <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> 17 </dependency> 18</dependencies>
属性配置(application.yml)
server: port: 9001 spring: application: name: service-hystrix-ribbon eureka: client: serviceUrl: defaultZone: http://admin:123456@localhost:8761/eureka/
开启hystrix功能,在启动类上加上@EnableHystrix注解
1 package com.carry.springcloud; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.loadbalancer.LoadBalanced; 6 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 import org.springframework.cloud.netflix.hystrix.EnableHystrix; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.web.client.RestTemplate; 10 11 @EnableHystrix 12 @EnableEurekaClient 13 @SpringBootApplication 14 public class ServiceHystrixRibbonApplication { 15 16 public static void main(String[] args) { 17 SpringApplication.run(ServiceHystrixRibbonApplication.class, args); 18 } 19 20 @Bean 21 @LoadBalanced 22 RestTemplate restTemplate() { 23 return new RestTemplate(); 24 } 25 }
添加fallback方法
package com.carry.springcloud.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; @RestController public class RibbonController { @Autowired RestTemplate restTemplate; @Value("${server.port}") String port; @GetMapping("/getPoducerInfo") @HystrixCommand(fallbackMethod = "getPoducerInfoFallback") public String getPoducerInfo() { String result = this.restTemplate.getForObject("http://service-producer/getPortInfo", String.class); return result; } public String getPoducerInfoFallback(){ return "getPoducerInfo异常,端口:" + port; } }
测试
依次启动eureka-server、service-producer、service-hystrix-ribbon,如下图成功注册到eureka
访问localhost:9001/getPoducerInfo,结果轮询显示8080与8081,停掉8081
接着访问localhost:9001/getPoducerInfo,当访问到8081时出现以下结果,说明服务降级调用了fallback方法
Feign 整合 Hystrix
新建项目并导入依赖
1 <dependencies> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-web</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>org.springframework.cloud</groupId> 8 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 9 </dependency> 10 <dependency> 11 <groupId>org.springframework.cloud</groupId> 12 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 13 </dependency> 14 <dependency> 15 <groupId>org.springframework.cloud</groupId> 16 <artifactId>spring-cloud-starter-openfeign</artifactId> 17 </dependency> 18</dependencies>
配置属性
server: port: 9002 spring: application: name: service-hystrix-feign eureka: client: serviceUrl: defaultZone: http://admin:123456@localhost:8761/eureka/ feign: hystrix: enabled: true
启动类开启Feign支持
1 package com.carry.springcloud; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.openfeign.EnableFeignClients; 6 7 @EnableFeignClients 8 @SpringBootApplication 9 public class ServiceHystrixFeignApplication { 10 11 public static void main(String[] args) { 12 SpringApplication.run(ServiceHystrixFeignApplication.class, args); 13 } 14 }
添加Feign接口
1 package com.carry.springcloud.api; 2 3 import org.springframework.cloud.openfeign.FeignClient; 4 import org.springframework.web.bind.annotation.GetMapping; 5 6 import com.carry.springcloud.fallback.ConsumerFeignFallback; 7 8 @FeignClient(name = "service-producer", fallback = ConsumerFeignFallback.class) 9 public interface ConsumerFeignClient { 10 11 @GetMapping("/getPortInfo") 12 public String produce(); 13 }
添加fallback类
1 package com.carry.springcloud.fallback; 2 3 import org.springframework.stereotype.Component; 4 5 import com.carry.springcloud.api.ConsumerFeignClient; 6 7 @Component 8 public class ConsumerFeignFallback implements ConsumerFeignClient { 9 10 @Override 11 public String produce() { 12 return "服务调用失败!"; 13 } 14 15 }
控制层注入Feign接口
1 package com.carry.springcloud.controller; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.GetMapping; 5 import org.springframework.web.bind.annotation.RestController; 6 7 import com.carry.springcloud.api.ConsumerFeignClient; 8 9 @RestController 10 public class ConsumerController { 11 12 @Autowired 13 private ConsumerFeignClient feignClient; 14 15 @GetMapping("/getPoducerInfoByFeign") 16 public String getPoducerInfoByFeign() { 17 return feignClient.produce(); 18 } 19 20 }
测试
测试方法同上不再赘述