共计 4402 个字符,预计需要花费 12 分钟才能阅读完成。
本系列代码地址:https://github.com/JoJoTec/sp…
接下来,将进入咱们降级之路的又一大模块,即网关模块。网关模块咱们废除了曾经进入保护状态的 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 还退出了异样解决和记录响应信息的逻辑:
HttpWebHandlerAdapter.java
// 交给内层解决封装好的 `ServerWebExchange`
return getDelegate().handle(exchange)
// 记录响应日志,trace 级别,个别用不上
.doOnSuccess(aVoid -> logResponse(exchange))
// 解决内层没有解决的异样,个别不会走到这里
.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
// 在所有解决实现后,将 response 设为 complete
.then(Mono.defer(response::setComplete));
剩下的内层的 WebHandler
,咱们将在下一节中持续剖析
微信搜寻“我的编程喵”关注公众号,每日一刷,轻松晋升技术,斩获各种 offer: