微服务系列:服务网关 Spring Cloud Gateway 熔断降级

今天继续来学习下 Spring Cloud Gateway 的熔断与降级。

在分布式系统中,网关做为流量的入口,所以会有大量的请求进入网关,向其余服务发起调用,其余服务不可避免的会出现调用失败(超时、异常),失败时不能让请求堆积在网关上,需要快速失败并返回给客户端,想要实现这个要求,就必须在网关上作熔断、降级操作。

概念介绍

服务降级:系统有限的资源的合理协调

  • 概念:服务降级一般是指在服务器压力剧增的时候,根据实际业务使用情况以及流量,对一些服务和页面有策略的不处理或者用一种简单的方式进行处理,从而释放服务器资源的资源以保证核心业务的正常高效运行。
  • 原因: 服务器的资源是有限的,而请求是无限的。在用户使用即并发高峰期,会影响整体服务的性能,严重的话会导致宕机,以至于某些重要服务不可用。故高峰期为了保证核心功能服务的可用性,就需要对某些服务降级处理。可以理解为舍小保大
  • 应用场景: 多用于微服务架构中,一般当整个微服务架构整体的负载超出了预设的上限阈值(和服务器的配置性能有关系),或者即将到来的流量预计会超过预设的阈值时(比如双11、6.18等活动或者秒杀活动)
  • 服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。
  • 需要考虑的问题:
    • 区分哪些服务为核心,哪些为非核心
    • 降级策略(处理方式,一般指如何给用户友好的提示或者操作)
    • 自动降级还是手动降
      微服务架构—服务降级 这个文章讲的很详细。

服务熔断:应对雪崩效应的链路自我保护机制。可看作降级的特殊情况

  • 概念:应对微服务雪崩效应的一种链路保护机制,类似股市、保险丝
  • 原因: 微服务之间的数据交互是通过远程调用来完成的。服务A调用服务,服务B调用服务c,某一时间链路上对服务C的调用响应时间过长或者服务C不可用,随着时间的增长,对服务C的调用也越来越多,然后服务C崩溃了,但是链路调用还在,对服务B的调用也在持续增多,然后服务B崩溃,随之A也崩溃,导致雪崩效应
  • 服务熔断是应对雪崩效应的一种微服务链路保护机制。例如在高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。同样,在微服务架构中,熔断机制也是起着类似的作用。当调用链路的某个微服务不可用或者响应时间太长时,会进行服务熔断,不再有该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
  • 服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。

Spring Cloud 框架里,熔断机制通过 Hystrix 实现。Hystrix 会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。

  • 应用场景:微服务架构中,多个微服务相互调用处使用

  • 需要考虑问题:

    • 如何所依赖的服务对象不稳定
    • 失败之后如何快速恢复依赖对象,如何探知依赖对象是否恢复

服务降级和服务熔断区别

  • 触发原因不一样,服务熔断由链路上某个服务引起的,服务降级是从整体的负载考虑
  • 管理目标层次不一样,服务熔断是一个框架层次的处理,服务降级是业务层次的处理
  • 实现方式不一样,服务熔断一般是自我熔断恢复,服务降级相当于人工控制
  • 触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;

一句话总结

服务熔断是应对系统服务雪崩的一种保险措施,给出的一种特殊降级措施。而服务降级则是更加宽泛的概念,主要是对系统整体资源的合理分配以应对压力。

服务熔断是服务降级的一种特殊情况,他是防止服务雪崩而采取的措施。系统发生异常或者延迟或者流量太大,都会触发该服务的服务熔断措施,链路熔断,返回兜底方法。这是对局部的一种保险措施。

服务降级是对系统整体资源的合理分配。区分核心服务和非核心服务。对某个服务的访问延迟时间、异常等情况做出预估并给出兜底方法。这是一种全局性的考量,对系统整体负荷进行管理。

代码实战

上述相关概念来自:服务降级与服务熔断区别 - 知乎 (zhihu.com)

了解完相关概念后,接下来我们看一下如何在 Spring Cloud Gateway 实战中配置熔断降级。

1、pom.xml 文件中添加 Hystrix 依赖

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>

这里需要注意下,Spring Cloud 版本的问题,版本不同可能引入不成功。我这里是 Hoxton.SR6 版本

2、application.yml 配置

server:
  port: 8080

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: cloud-gateway
          uri: http://localhost:8080
          predicates:
            - Path=/ytb/**
          filters:
            - StripPrefix=1
            # 降级配置
            - name: Hystrix
              args:
                name: testOne
                # 降级接口的地址
                fallbackUri: forward:/fallback
# 针对全局配置
hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 3000
    # 对单独的 Hystrix 的 commandKey 设置超时时间
    testOne:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000

配置中有一个可选参数fallbackUri,当前只支持forward模式的URI。如果触发熔断,请求会被转发到该URI对应的控制器。控制器可以是自定义的fallback接口;也可以是自定义的Handler,需要实现接口org.springframework.web.reactive.function.server.HandlerFunction

还设置了接口超时时间,也就是接口响应超过这个时间就会触发熔断。

3、fallbackUri 配置的降级地址接口

@RestController
public class FallbackController {

    @GetMapping("/fallback")
    public Map<String, Object> fallback() {
        Map<String, Object> map = new HashMap<>();
        map.put("code", "error");
        map.put("msg", "服务暂时不可用");
        return map;
    }
}

4、测试接口

@RestController
public class TestController {

    @GetMapping("/timeout")
    public String timeout(){
        try {
            Thread.sleep(5000);
            System.out.println("模拟接口超时");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "请求成功";
    }
}

Thread.sleep(5000) 用来模拟接口调用超时。

然后就可以启动项目,访问这个地址 localhost:8080/ytb/timeout

会得到以下返回结果:

微服务系列:服务网关 Spring Cloud Gateway 熔断降级_第1张图片

因为我们设置的超时时间是 3 秒,接口睡眠 5 秒,所以就触发了降级熔断。

此时我们 application.yml 中修改一下超时时间为 6 秒,再次访问

微服务系列:服务网关 Spring Cloud Gateway 熔断降级_第2张图片

这次就没有触发降级熔断了。

5、自定义 Handler 方式

前文提到控制器可以是自定义的fallback接口;也可以是自定义的Handler。我再贴上 Handler 方式的代码

路由配置:

@Configuration
public class RouterFunctionConfiguration
{
    @Autowired
    private HystrixFallbackHandler hystrixFallbackHandler;

    @SuppressWarnings("rawtypes")
    @Bean
    public RouterFunction routerFunction()
    {
        return RouterFunctions
                .route(RequestPredicates.path("/fallback").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
                        hystrixFallbackHandler);
    }
}

自定义 Handler

@Component
public class HystrixFallbackHandler implements HandlerFunction<ServerResponse>
{
    private static final Logger log = LoggerFactory.getLogger(HystrixFallbackHandler.class);

    @Override
    public Mono<ServerResponse> handle(ServerRequest serverRequest)
    {
        Optional<Object> originalUris = serverRequest.attribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        originalUris.ifPresent(originalUri -> log.error("网关执行请求:{}失败,hystrix服务降级处理", originalUri));
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(JSON.toJSONString("{"error":"服务已被降级熔断"}")));
    }
}

启动网关服务GatewayApplication.java,访问localhost:8080/ytb/timeout 再进行测试,会发现返回服务已被降级熔断

至此,本文结束。后续可能会有补充

你可能感兴趣的:(微服务系列,微服务,gateway,java,熔断降级)