SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(1)

接下来,将进入我们升级之路的又一大模块,即网关模块。网关模块我们废弃了已经进入维护状态的 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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-parent</artifactId>
<groupId>com.github.jojotech</groupId>
<version>2020.0.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-api-gateway</artifactId>
<dependencies>
<dependency>
<groupId>com.github.jojotech</groupId>
<artifactId>spring-cloud-webflux</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
</project>
parent 指向了我们项目的 spring-cloud-parent,同时加入了上一节实现的 spring-cloud-webflux 依赖,同时还需要加入 spring-cloud-starter-gateway,由于在我们的 spring-cloud-parent 已经指定了 spring-cloud-parent 的版本依赖管理,所以这里不需要指定 spring-cloud-starter-gateway 的版本
然后,我们开始编写配置文件:
application.yml
server:
##端口为 8181
port: 8181
spring:
application:
# 微服务名称是 apiGateway
name: apiGateway
cloud:
gateway:
httpclient:
# 网关转发到其他微服务的 HTTP 连接超时为 500ms
connect-timeout: 500
# 网关转发到其他微服务的 HTTP 响应超时为 500ms
response-timeout: 60000
routes:
# 编写转发规则
- id: first_route
# 转发到微服务 test-service
uri: lb://test-service
# 包含哪些路径
predicates:
- Path=/test-ss/**
# 转发到的微服务访问路径,去掉路径中的第一块,即去掉 /test-ss
filters:
- StripPrefix=1
loadbalancer:
# 指定 zone,因为我们之前在负载均衡中加入了只有同一个 zone 的实例才能互相访问的逻辑
zone: test
ribbon:
# 关闭ribbon
enabled: false
cache:
# 本地微服务实例列表缓存时间
ttl: 5
# 缓存大小,你的微服务调用多少个其他微服务,大小就设置为多少,默认256
capacity: 256
discovery:
client:
simple:
# 使用 spring-common 中的简单 DiscoveryClient 服务发现客户端,就是将微服务实例写死在配置文件中
instances:
# 指定微服务 test-service 的实例列表
test-service:
- host: httpbin.org
port: 80
metadata:
# 指定该实例的 zone,因为我们之前在负载均衡中加入了只有同一个 zone 的实例才能互相访问的逻辑
zone: test
eureka:
client:
# 关掉 eureka
enabled: false
最后编写启动入口类:
package com.github.jojotech.spring.cloud.apigateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = "com.github.jojotech.spring.cloud.apigateway")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
启动,访问路径: 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 的接口定义是:
public interface WebHandler {
Mono<Void> handle(ServerWebExchange exchange);
}
但是最外层传进来的参数是 request 和 response,需要将他们封装成 ServerWebExchange,这个工作就是在 HttpWebHandlerAdapter 中做的。HttpWebHandlerAdapter 其实主要任务就是将各种参数封装成 ServerWebExchange(除了和本次请求相关的 request 和 response,还有会话管理器 SessionManager,编码解码器配置,国际化配置还有 ApplicationContext 用于扩展)。
除了这些,处理 Forwarded 还有 X-Forwarded* 相关的 Header 的配置逻辑,也在这里进行。然后将封装好的 ServerWebExchange 交给内层的 WebHandler 即 ExceptionHandlingWebHandler 继续处理。同时,从源码中可以看出,交给内层处理的 Mono 还加入了异常处理和记录响应信息的逻辑:
//交给内层处理封装好的 `ServerWebExchange`
return getDelegate().handle(exchange)
//记录响应日志,trace 级别,一般用不上
.doOnSuccess(aVoid -> logResponse(exchange))
//处理内层没有处理的异常,一般不会走到这里
.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
//在所有处理完成后,将 response 设为 complete
.then(Mono.defer(response::setComplete));
剩下的内层的 WebHandler,我们将在下一节中继续分析
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:

SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(1)的更多相关文章
- SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(2)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 我们继续分析上一节提到的 WebHandler,经过将请求封装成 ServerWebExc ...
- SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(3)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 我们继续分析上一节提到的 WebHandler.加入 Spring Cloud Sleut ...
- SpringCloud升级之路2020.0.x版-42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 网关由于是所有外部用户请求的入口,记录这些请求中我们需要的元素,对于线上监控以及业务问题定 ...
- SpringCloud升级之路2020.0.x版-1.背景
本系列为之前系列的整理重启版,随着项目的发展以及项目中的使用,之前系列里面很多东西发生了变化,并且还有一些东西之前系列并没有提到,所以重启这个系列重新整理下,欢迎各位留言交流,谢谢!~ Spring ...
- SpringCloud升级之路2020.0.x版-6.微服务特性相关的依赖说明
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford spring-cl ...
- SpringCloud升级之路2020.0.x版-10.使用Log4j2以及一些核心配置
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 我们使用 Log4 ...
- SpringCloud升级之路2020.0.x版-43.为何 SpringCloudGateway 中会有链路信息丢失
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在开始编写我们自己的日志 Filter 之前,还有一个问题我想在这里和大家分享,即在 Sp ...
- SpringCloud升级之路2020.0.x版-29.Spring Cloud OpenFeign 的解析(1)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在使用云原生的很多微服务中,比较小规模的可能直接依靠云服务中的负载均衡器进行内部域名与服务 ...
- SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(1)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在前面一节,我们利用 resilience4j 粘合了 OpenFeign 实现了断路器. ...
随机推荐
- Java中的基本类型和包装类
Java中基本数据类型与包装类型有 基本类型 包装器类型 boolean Boolean char Character int Integer byte Byte short Shor ...
- JVM详解(四)——运行时数据区-堆
一.堆 1.介绍 Java运行程序对应一个进程,一个进程就对应一个JVM实例.一个JVM实例就有一个运行时数据区(Runtime),Runtime里面,就只有一个堆,一个方法区.这里也阐述了,方法区和 ...
- PAT (Basic Level) Practice (中文)1025 反转链表 (25分)
1025 反转链表 (25分) 给定一个常数 K 以及一个单链表 L,请编写程序将 L 中每 K 个结点反转.例如:给定 L 为 1→2→3→4→5→6,K 为 3,则输出应该为 3→2→1→6→5→ ...
- 【Spring】IoC容器 - Spring Bean作用域Scope(含SpringCloud中的RefreshScope )
前言 上一章学习了[依赖来源],本章主要讨论SpringBean的作用域,我们这里讨论的Bean的作用域,很大程度都是默认只讨论依赖来源为[Spring BeanDefinition]的作用域,因为在 ...
- activiti会签 多实例例子
在实际的业务中,可能存在存在这么一种情况,当流程运行到某一个环节时,可能需要同时多个人的参与,才可以完成此环节.此时就可以用到activiti的多实例来解决此问题. 一.将一个节点设置成多实例的方法: ...
- DMA实现采样数据的直接搬运存储
尝试了下STM32的ADC采样,并利用DMA实现采样数据的直接搬运存储,这样就不用CPU去参与操作了. 找了不少例子参考,ADC和DMA的设置了解了个大概,并直接利用开发板来做一些实验来验证相关的操作 ...
- [源码解析] Pytorch 如何实现后向传播 (3)---- 引擎动态逻辑
[源码解析] Pytorch 如何实现后向传播 (3)---- 引擎动态逻辑 目录 [源码解析] Pytorch 如何实现后向传播 (3)---- 引擎动态逻辑 0x00 摘要 0x01 前文回顾 0 ...
- Springboot第一次访问慢,自身缺陷问题?
一.现象: 1.访问controller,第一次速度在300-400ms,第二次访问就很快了大概在20ms,相差几十倍,是哪里出了问题,尝试了网上很多教程都没有作用 如启动参数设置 -Djava.se ...
- 关于 better-scroll 设置了以后无法滚动或不生效的问题
首先在mounted里面注册组件 例:let scroll = new BScroll("#commondityLeftList") 然后打印实例化对象,例:console. ...
- 聊聊sql优化的15个小技巧
前言 sql优化是一个大家都比较关注的热门话题,无论你在面试,还是工作中,都很有可能会遇到. 如果某天你负责的某个线上接口,出现了性能问题,需要做优化.那么你首先想到的很有可能是优化sql语句,因为它 ...