这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战
本系列代码地址:github.com/JoJoTec/spr…
接下来,将进入我们升级之路的又一大模块,即网关模块。网关模块我们废弃了已经进入维护状态的 zuul,选用了 Spring Cloud Gateway 作为内部网关。为何选择 Spring Cloud Gateway 而不是 nginx 还有 Kong 的原因是:
- 项目组对于 Java 更加熟悉,并且对于 Project Reactor 异步编程也比较熟悉,这个比较重要
- 需要在网关中使用我们之前实现的基于请求的有状态重试的压力敏感的负载均衡器
- 需要在网关中实现重试
- 需要在网关中实现实例路径断路
- 需要在网关中进行业务统一加解密
- 需要在网关中实现 BFF(Backends For Frontends)接口,即根据客户端请求,将某几个不同接口的请求一次性组合返回
- 需要在网关中使用 Redis 记录一些与 Token 相关的值
因此,我们使用了 Spring Cloud Gateway 作为内部网关,接下来,我们就来依次实现上面说的这些功能。同时在本次升级使用过程中, Spring Cloud Gateway 也有一些坑,例如:
- 结合使用 spring-cloud-sleuth 会有链路信息追踪,但是某些情况链路信息会丢失。
- 对于三方 Reactor 封装的异步 API (例如前面提到的操作 Redis 使用的 spring-data-redis)理解不到位导致关键线程被占用。
但是首先,我们需要简单理解下 Spring Cloud Gateway 究竟包括哪些组件以及整个调用流程是什么样子的。由于 Spring Cloud Gateway 基于 Spring-Boot 和 Spring-Webflux 实现,所以我们会从外层 WebFilter 开始说明,然后分析如何走到 Spring Cloud Gateway 的封装逻辑,以及 Spring Cloud Gateway 包含的组件,请求是如何转发出去,回来后又经过了哪些处理,这些我们都会逐一分析。
创建一个简单的 API 网关
为了详细分析流程,我们先来创建一个简单的网关,用于快速上手并分析。
首先创建依赖:
pom.xml
1 | xml复制代码<?xml version="1.0" encoding="UTF-8"?> |
parent 指向了我们项目的 spring-cloud-parent,同时加入了上一节实现的 spring-cloud-webflux 依赖,同时还需要加入 spring-cloud-starter-gateway,由于在我们的 spring-cloud-parent 已经指定了 spring-cloud-parent 的版本依赖管理,所以这里不需要指定 spring-cloud-starter-gateway 的版本
然后,我们开始编写配置文件:
application.yml
1 | yaml复制代码server: |
最后编写启动入口类:
1 | typescript复制代码package com.github.jojotech.spring.cloud.apigateway; |
启动,访问路径: http://127.0.0.1:8181/test-ss/anything
,可以看到请求被发送到 httpbin.org 的 anything 路径中,这个接口会返回请求中的所有信息。
这样,我们就实现了一个简单的网关。接下来我们来详细分析其工作流程和源码。
异步环境下请求处理的核心 - Spring Boot + Spring WebFlux 的 WebHandler
我们创建的简易网关,外层的服务容器其实就是基于 Netty 和 Project Reactor 的容器,我们跳过这些,直接进入 Spring Boot 相关的处理逻辑。我们只需要知道,请求和其对应的响应,会被外层的容器封装成为 ServerHttpRequest request
和 ServerHttpResponse response
(都在 org.springframework.http.server.reactive
这个包下)。
然后,会交由 WebHandler
进行处理。WebHandler
的实现,其实是一种责任链装饰模式,如下图所示。每一层的 WebHandler
会将 request
和 response
进行对应自己责任的装饰,然后交给内层的 WebHandler
处理。
HttpWebHandlerAdapter - 将请求封装成 ServerWebExchange
WebHandler
的接口定义是:
1 | kotlin复制代码public interface WebHandler { |
但是最外层传进来的参数是 request
和 response
,需要将他们封装成 ServerWebExchange
,这个工作就是在 HttpWebHandlerAdapter 中做的。HttpWebHandlerAdapter 其实主要任务就是将各种参数封装成 ServerWebExchange
(除了和本次请求相关的 request
和 response
,还有会话管理器 SessionManager,编码解码器配置,国际化配置还有 ApplicationContext 用于扩展)。
除了这些,处理 Forwarded
还有 X-Forwarded*
相关的 Header 的配置逻辑,也在这里进行。然后将封装好的 ServerWebExchange
交给内层的 WebHandler
即 ExceptionHandlingWebHandler
继续处理。同时,从源码中可以看出,交给内层处理的 Mono 还加入了异常处理和记录响应信息的逻辑:
1 | scss复制代码//交给内层处理封装好的 `ServerWebExchange` |
剩下的内层的 WebHandler
,我们将在下一节中继续分析
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:
本文转载自: 掘金