分布式微服务架构面临的问题:
在微服务架构中,根据业务来拆分成一个个的服务,而服务与服务之间存在着依赖关系 (比如用户调商品, 商品调库存,库存调订单等等),在Spring Cloud中多个微服务之间可以用 RestTemplate+Ribbon 和 Feign来调用。
雪崩效应
在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问A服务,而A服务需要调用B服务,B服务需要调用C服务,由于网络原因或者自身的原因,如果B服务或者C服务不能及时响应,A服务将处于阻塞状态,直到B服务C服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
雪崩是系统中的蝴蝶效应导致其发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法响应变慢,亦或是某台机器的资源耗尽。从源头上我们无法完全杜绝雪崩源头的发生,但是雪崩的根本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。
1 服务熔断
什么是服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图:
当服务之间调用的链路上某个微服务不可用或者响应时间太长时,会导致连锁故障。当失败的调用到一定阈值(缺省是5秒内20次调用失败) 就会启动熔断机制。在 SpringCloud 框架里熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的状况。熔断机制的注解是 @HystrixCommand
熔断器打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值。
案例:
第一种方式 在服务提供者处通过@HystrixCommand实现熔断
pom:
- <!-- 导入hystrix依赖 -->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
- </dependency>
-
修改 ProductController控制器:
出现异常后如何处理? 使用 @HystrixCommand 注解 一旦调用服务方法失败并抛出了错误信息后,会自动调用 @HystrixCommand 注解中 fallbackMethod 属性指定的当前类中的方法
- @RestController
- public class ProductController {
-
- @Autowired
- private ProductService productService;
-
- @RequestMapping(value = "/product/add", method = RequestMethod.POST)
- public boolean add(@RequestBody Product product) {
- return productService.add(product);
- }
-
- @RequestMapping(value = "/product/get/{id}", method = RequestMethod.GET)
- //当get方法出现异常时,则会调用hystrixGet方法处理
- @HystrixCommand(fallbackMethod = "getFallback")
- public Product get(@PathVariable("id") Long id) {
- System.out.println("=========+执行了==========");
- return productService.get(id);
- }
-
- @RequestMapping(value = "/product/list", method = RequestMethod.GET)
- public List<Product> list() {
- return productService.list();
- }
-
- //当get方法出现异常时,则会调用此方法. 注意此方法的返回值,参数列表与原方法一致
- public Product getFallback(@PathVariable("id") Long id) {
- return new Product(id, "ID=" + id + "无效--@HystrixCommand", "无有效数据库");
- }
-
- }
-
修改启动类添加@EnableHystrix:
如果我们采用服务熔断机制,那么服务提供者有多少个方法,我们就要写多少个服务熔断的注解,同时也要写多少个服务熔断的处理方法
这样就可能导致方法通胀,我们使用服务降级来实现解耦,我们可以通过面向接口的方式实现服务降级,类似于feign客户端接口调用
第二种方式: Feign 客户端服务熔断
Feign 是自带断路器的,也就是针对 消费者(客户端)进行服务熔断,需要在配置文件中开启它,在配置文件加以 下代码:
- # 需要开启 hystrix
- feign:
- hystrix:
- enabled: true
-
只要在已存在的 ProductClientService 接口上的 @FeignClient 注解中,加上 fallback 指定熔断处理类即可: ProductClientServiceFallBack.class
- //fallback 指定熔断处理类,如果被调用的方法出现异常,就会交给熔断处理类中的对应方法进行处理
- @FeignClient(value = "microservice-product",fallback = ProductClientServiceFallback.class)
- public interface ProductClientService {
-
- @RequestMapping(value = "/product/add", method = RequestMethod.POST)
- boolean add(@RequestBody Product product);
-
- @RequestMapping(value = "/product/get/{id}", method = RequestMethod.GET)
- Product get(@PathVariable("id") Long id);
-
- @RequestMapping(value = "/product/list", method = RequestMethod.GET)
- List<Product> list();
-
- }
-
创建 ProductClientServiceFallBack 类,并实现ProductClientService 接口
注意使用 @Component 注解将它注入到容器中
- @Component
- public class ProductClientServiceFallback implements ProductClientService{
- @Override
- public boolean add(Product product) {
- return false;
- }
-
- @Override
- public Product get(Long id) {
- return new Product(id, "id=" + id + "无数据--@feignclient&hystrix", "无有效数据库");
- }
-
- @Override
- public List<Product> list() {
- return null;
- }
- }
-