分布式微服务架构面临的问题:
在微服务架构中,根据业务来拆分成一个个的服务,而服务与服务之间存在着依赖关系 (比如用户调商品, 商品调库存,库存调订单等等),在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;
}
}