文章
问答
冒泡
Spring Cloud 系列之微服务的容错处理 Hystrix(七)

总结前面几篇实际上我们已经经历过了 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

springcloud

关于作者

Kirago
个人站点 https://kiragoo.github.io/
获得点赞
文章被阅读