总结前面几篇实际上我们已经经历过了 RestTemplate(restful api 请求构造)、Eureka (服务发现与注册)、Ribbon(负载均衡)、Fegin(声明式Rest)。基础的内容算是有了大概的了解,虽然 demo 例子比较简单,其中也适当了引入了自己对这部分内容的理解与拓展思考。但是仅仅拥有以上的知识栈其实我觉得远远是不够的,肯定是不能够上生产的微服务应用。
此篇的内容实际上就是微服务处理中的高级话题-容错处理。
雪崩效应
在实际的微服务架构中,各个服务之间肯定会存在依赖关系。在生产环境中,对于网络来说可能是脆弱的,那么“基础服务故障”会导致“级联故障”从而引起“雪崩效应”。
如何解决雪崩效应
容错机制是一种普遍采用的方案
容错机制在我理解看来实际上就是一种“预留可用资源”的方案,在应对可能产生雪崩效应的场景下,我们通过及时响应,保证系统资源不要被“无谓的竞争”消耗,从而保证始终有那么“一批资源空间”能够供使用。
常见方案有以下两种:
- 为网络请求设置超时:如果一个请求可能因为某种因素导致响应很慢,那么此次的远程调用对应的一个线程/进程就会被长时间占用,那么在此时间窗口期可能会有多个这样的情况,夸张的想下肯定会出现服务资源会被耗尽的情况,最终导致服务完全不可用了。
- 使用断路器模式:此场景下可谓更夸张些,在实际的场景中,可能就是某个微服务直接不可用,那么如果再次对此微服务进行请求那么就是没有任何意义的,只会无畏的消耗资源。
断路器模式可简单的理解为对容易导致错误的操作的代理,该代理能够统计一段时间内调用的失败的次数。实际上存在了可控指标之后我们做的事情就变得灵活多了,我们可实现快速断路,也能够实现“自我恢复”的功能。
Hystrix 实现容错
Hystrix 通过如下几点实现延时和容错:
- 包裹请求:使用 HystrixCommand 包裹对依赖的调用逻辑,每个命令在独立线程中执行,使用了设计模式中的“命令模式”
- 跳闸机制:这种理解起来比较容易,实际上交就是判断某服务的错误率达到一定的阈值之后,Hystrix 可以自动或者手动的“跳闸”,停止请求该服务的一段时间的请求。
- 资源隔离:这种概念也容易理解,即 Hystrix 通过为每个依赖维护一个小型的线程池(或者信号量)。如果该线程池已满,那么发往依赖服务的请求直接被拒绝
- 监控:即通过几乎于实时的监控运行指标和配置的变化来控制容错实现
- 回退机制:理解及字面意思,回退逻辑可由开发者自行定义
- 自我修复
消费者服务
microservice-consumer-movie-ribbon-hystrix
将 microservice-consumer-movie-ribbon 服务改造为 microservice-consumer-movie-ribbon-hystrix
- pom 文件追加如下内容:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
- controller 层:
package com.kirago.sc.microserverconsumermovieribbon.controller; import com.kirago.sc.microserverconsumermovieribbon.entity.User; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import com.kirago.sc.microserverconsumermovieribbon.config.RestTemplateCompoment; @RestController public class MovieController { private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class); @Autowired private RestTemplateCompoment restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @HystrixCommand(fallbackMethod = "findByIdFallback") //引入回退函数 @GetMapping("/user/{id}") public User findById(@PathVariable Long id){ return this.restTemplate.restTemplate().getForObject("http://microservice-provider-user-ribbon/" + id, User.class); } @GetMapping("/log-user-instance") public void logUserInstance() { ServiceInstance serviceInstance = loadBalancerClient.choose("microservice-provider-user-ribbon"); MovieController.LOGGER.info("=====ServiceId:{},ServiceHost:{}.ServicePort:{}===", serviceInstance.getServiceId(), serviceInstance.getHost(), serviceInstance.getPort()); } public User findByIdFallback(Long id) { //回退函数 User user = new User(); user.setId(-1L); user.setName("默认用户"); return user; } // public User findByIdFallback(Long id, Throwable throwable) { // // LOGGER.error("进入回退方法,异常:", throwable); // // User user = new User(); // user.setId(-1L); // user.setName("默认用户"); // return user; } }
@HystrixCommand 的配置拓展
@HystrixCommand(fallbackMethod = "findByIdFallback", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"), @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000") }, threadPoolProperties = { @HystrixProperty(name = "coreSize", value = 1), @HystrixProperty(name = "maxQueueSize", value = 10) })
其他两个服务模块分别为 microservice-provider-user、microservice-discovery-eureka, 然后分别启动三种服务模块,最终的 Eureka Server Protal 页面运行正常的话会显示如下:
验证
发送请求进行验证:
- 生产者服务可用的情况:
- 此时关闭生产者服务,即生产者服务不可用,发送请求如下:
场景引申:
- 在很多场景下,我们是需要获得回退的原因的,那么我们只需在 fallbackMethos 加一个 Throwable 的参数即可。
public User findByIdFallback(Long id, Throwable throwable) { LOGGER.error("进入回退方法,异常:", throwable); User user = new User(); user.setId(-1L); user.setName("默认用户"); return user; }
- 另外我们也可以通过配置 fallbackMethod 的属性,实现在某些异常情况下不触发 回退函数。 @HystrixCommand 提供了 ignoreExceptions 属性
@HystrixCommand(fallbanckMethod = "findByIdFallback", ignoreExceptions ={IllegalArgumentException.class, MybusinessException.class}} )
-------- 分割线--------
本章节示例代码: chapter5