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

    概念介绍
    服务降级:系统有限的资源的合理协调
    概念:服务降级一般是指在服务器压力剧增的时候,根据实际业务使用情况以及流量,对一些服务和页面有策略的不处理或者用一种简单的方式进行处理,从而释放服务器资源的资源以保证核心业务的正常高效运行。
    原因: 服务器的资源是有限的,而请求是无限的。在用户使用即并发高峰期,会影响整体服务的性能,严重的话会导致宕机,以至于某些重要服务不可用。故高峰期为了保证核心功能服务的可用性,就需要对某些服务降级处理。可以理解为舍小保大
    应用场景: 多用于微服务架构中,一般当整个微服务架构整体的负载超出了预设的上限阈值(和服务器的配置性能有关系),或者即将到来的流量预计会超过预设的阈值时(比如双11、6.18等活动或者秒杀活动)
    服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。
    需要考虑的问题:
    区分哪些服务为核心,哪些为非核心
    降级策略(处理方式,一般指如何给用户友好的提示或者操作)
    自动降级还是手动降
    微服务架构—服务降级 这个文章讲的很详细。
    服务熔断:应对雪崩效应的链路自我保护机制。可看作降级的特殊情况
    概念:应对微服务雪崩效应的一种链路保护机制,类似股市、保险丝
    原因: 微服务之间的数据交互是通过远程调用来完成的。服务A调用服务,服务B调用服务c,某一时间链路上对服务C的调用响应时间过长或者服务C不可用,随着时间的增长,对服务C的调用也越来越多,然后服务C崩溃了,但是链路调用还在,对服务B的调用也在持续增多,然后服务B崩溃,随之A也崩溃,导致雪崩效应
    服务熔断是应对雪崩效应的一种微服务链路保护机制。例如在高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。同样,在微服务架构中,熔断机制也是起着类似的作用。当调用链路的某个微服务不可用或者响应时间太长时,会进行服务熔断,不再有该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
    服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。
    在 Spring Cloud 框架里,熔断机制通过 Hystrix 实现。Hystrix 会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。

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

    需要考虑问题:

    如何所依赖的服务对象不稳定
    失败之后如何快速恢复依赖对象,如何探知依赖对象是否恢复
    服务降级和服务熔断区别
    触发原因不一样,服务熔断由链路上某个服务引起的,服务降级是从整体的负载考虑
    管理目标层次不一样,服务熔断是一个框架层次的处理,服务降级是业务层次的处理
    实现方式不一样,服务熔断一般是自我熔断恢复,服务降级相当于人工控制
    触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
    一句话总结
    服务熔断是应对系统服务雪崩的一种保险措施,给出的一种特殊降级措施。而服务降级则是更加宽泛的概念,主要是对系统整体资源的合理分配以应对压力。

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

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

    代码实战

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

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

    1. <dependency>
    2. <groupId>org.springframework.cloud</groupId>
    3. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    4. </dependency>

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

    2、application.yml 配置

    1. server:
    2. port: 8080
    3. spring:
    4. application:
    5. name: api-gateway
    6. cloud:
    7. gateway:
    8. routes:
    9. - id: cloud-gateway
    10. uri: http://localhost:8080
    11. predicates:
    12. - Path=/ytb/**
    13. filters:
    14. - StripPrefix=1
    15. # 降级配置
    16. - name: Hystrix
    17. args:
    18. name: testOne
    19. # 降级接口的地址
    20. fallbackUri: forward:/fallback
    21. # 针对全局配置
    22. hystrix:
    23. command:
    24. default:
    25. execution:
    26. timeout:
    27. enabled: true
    28. isolation:
    29. thread:
    30. timeoutInMilliseconds: 3000
    31. # 对单独的 Hystrix 的 commandKey 设置超时时间
    32. testOne:
    33. execution:
    34. isolation:
    35. thread:
    36. timeoutInMilliseconds: 3000

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

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

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

    1. @RestController
    2. public class FallbackController {
    3. @GetMapping("/fallback")
    4. public Map<String, Object> fallback() {
    5. Map<String, Object> map = new HashMap<>();
    6. map.put("code", "error");
    7. map.put("msg", "服务暂时不可用");
    8. return map;
    9. }
    10. }

    4、测试接口

    1. @RestController
    2. public class TestController {
    3. @GetMapping("/timeout")
    4. public String timeout(){
    5. try {
    6. Thread.sleep(5000);
    7. System.out.println("模拟接口超时");
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. return "请求成功";
    12. }
    13. }

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

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

    会得到以下返回结果:

    image.png

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

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

    image.png

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

    5、自定义 Handler 方式

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

    路由配置:

    1. @Configuration
    2. public class RouterFunctionConfiguration
    3. {
    4. @Autowired
    5. private HystrixFallbackHandler hystrixFallbackHandler;
    6. @SuppressWarnings("rawtypes")
    7. @Bean
    8. public RouterFunction routerFunction()
    9. {
    10. return RouterFunctions
    11. .route(RequestPredicates.path("/fallback").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
    12. hystrixFallbackHandler);
    13. }
    14. }

    自定义 Handler:

    1. @Component
    2. public class HystrixFallbackHandler implements HandlerFunction<ServerResponse>
    3. {
    4. private static final Logger log = LoggerFactory.getLogger(HystrixFallbackHandler.class);
    5. @Override
    6. public Mono<ServerResponse> handle(ServerRequest serverRequest)
    7. {
    8. Optional<Object> originalUris = serverRequest.attribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
    9. originalUris.ifPresent(originalUri -> log.error("网关执行请求:{}失败,hystrix服务降级处理", originalUri));
    10. return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).contentType(MediaType.APPLICATION_JSON)
    11. .body(BodyInserters.fromValue(JSON.toJSONString("{"error":"服务已被降级熔断"}")));
    12. }
    13. }

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