版本信息
- java版本:17.0.8
- maven版本 3.9.2
- 操作系统:windows11
- 项目依赖版本如下:
<properties>
<java.version>1.8</java.version>
<lombok.version>1.18.26</lombok.version>
<log4j.version>1.2.17</log4j.version>
<fastjson2.version>2.0.14</fastjson2.version>
<fastjson.version>1.2.49</fastjson.version>
<hutool-all.version>5.7.13</hutool-all.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<jjwt.version>0.9.0</jjwt.version>
<nimbus-jose-jwt.version>9.23</nimbus-jose-jwt.version>
<tencentcloud-sdk-java.version>3.1.270</tencentcloud-sdk-java.version>
<spring-boot-maven-plugin.version>2.7.3</spring-boot-maven-plugin.version>
<spring-cloud-oauth2.version>2.2.5.RELEASE</spring-cloud-oauth2.version>
<mybatis-spring-boot.version>2.2.2</mybatis-spring-boot.version>
<pagehelper-spring-boot.version>1.3.0</pagehelper-spring-boot.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
<!-- 覆盖SpringBoot中okhttp3的旧版本声明,解决MinIO 8.3.x的依赖冲突 -->
<okhttp3.version>4.8.1</okhttp3.version>
<!-- spring-boot原本的okhttp版本 -->
<okhttp.version>3.14.9</okhttp.version>
<spring-boot.version>2.6.12</spring-boot.version>
<spring-boot-test.version>2.6.12</spring-boot-test.version>
<spring-boot-security.version>2.6.12</spring-boot-security.version>
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
<alibaba.nacos.version>1.4.2</alibaba.nacos.version>
</properties>
父项目完整pom如下:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-cloud-learning</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>spring-cloud-learning</name>
<properties>
<java.version>1.8</java.version>
<lombok.version>1.18.26</lombok.version>
<log4j.version>1.2.17</log4j.version>
<fastjson2.version>2.0.14</fastjson2.version>
<fastjson.version>1.2.49</fastjson.version>
<hutool-all.version>5.7.13</hutool-all.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<jjwt.version>0.9.0</jjwt.version>
<nimbus-jose-jwt.version>9.23</nimbus-jose-jwt.version>
<tencentcloud-sdk-java.version>3.1.270</tencentcloud-sdk-java.version>
<spring-boot.version>2.6.12</spring-boot.version>
<spring-boot-maven-plugin.version>2.7.3</spring-boot-maven-plugin.version>
<spring-cloud-oauth2.version>2.2.5.RELEASE</spring-cloud-oauth2.version>
<mybatis-spring-boot.version>2.2.2</mybatis-spring-boot.version>
<pagehelper-spring-boot.version>1.3.0</pagehelper-spring-boot.version>
<spring-boot-test.version>2.6.12</spring-boot-test.version>
<spring-boot-security.version>2.6.12</spring-boot-security.version>
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
<alibaba.nacos.version>1.4.2</alibaba.nacos.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
<!-- 覆盖SpringBoot中okhttp3的旧版本声明,解决MinIO 8.3.x的依赖冲突 -->
<okhttp3.version>4.8.1</okhttp3.version>
<!-- spring-boot原本的okhttp版本 -->
<okhttp.version>3.14.9</okhttp.version>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<!--
https://start.spring.io/actuator/info
cloud版本一定要和boot版本匹配
必须以这种方式集成cloud-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot-test.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring cloud alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-maven-plugin.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
新建名为spring-cloud-gateway的子模块
项目结构如下图:
经典配置中的核心概念
- 先通过一个典型的简化版配置来了解几个核心概念,假设Spring Cloud Gateway应用正在运行,监听
10005端口,一旦有远程请求来到10005端口,下面的配置就会生效了,三个核心概念,以及每个配置的作用,请参考中文注释:
server:
port: 10005
spring:
cloud:
gateway:
# 核心概念1:路由,一个路由代表一个处理逻辑,
# 该逻辑里面包含三个元素:匹配条件(是否该此路由处理)、真实处理地址、过滤器
routes:
# id要确保唯一性
- id: add_request_header_route
# 真实处理地址,请求一旦确定是当前路由处理,就会转发到这个地址去
uri: https://example.org
# 核心概念2:谓语或者断言,作用是判断请求是否由当前路由处理
predicates:
# 这是断言的一种,检查请求的Cookie中mycookie的值是否等于mycookievalue
- Cookie=mycookie,mycookievalue
# 核心概念3:过滤器,请求前和请求后都可以有过滤器处理请求响应数据
filters:
# 这个过滤器的作用是在请求header中添加一个键值对,值等于"aaabbbccc"
- AddRequestHeader=X-Request-Red, aaabbbccc
- 上述配置信息中的predicates是简化版配置,和完整配置对比效果如下,简单的说就是把一行拆成了三项:name、args.name、args.regexp
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 注册中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- bootstrap 文件生效 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- 配置
bootstrap.yml
server:
port: 10005
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: path_route
# 匹配成功后,会被转发到8082端口,至于端口后面的path,会直接使用原始请求的
# 例如http://127.0.0.1:10005/nacos/test,会被转发到http://127.0.0.1:9001/nacos/test
uri: http://127.0.0.1:9001
predicates:
# 根据请求路径中带有"/nacos/",就算匹配成功
- Path=/nacos/**
discovery:
locator:
# 开启这个,可以使用lb://nacos服务列表的服务名,转发请求
enabled: true
nacos:
discovery:
server-addr: localhost:8848
- 如果要转发到其他域名下,需要创建配置类解决跨域问题:
package com.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
- 启动类:
package com.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
- 最后是单元测试类,请注意,由于Spring Cloud Gateway使用了webflux技术栈,因此不能用常见的MockMvc来模拟请求,几个注解也值得注意,另外也要注意WebTestClient的expectStatus、expectBody等API的用法:
package com.gateway;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureWebTestClient
public class GatewayTest {
@Autowired
private WebTestClient webClient;
@Test
void testHelloPredicates() {
webClient.get()
.uri("/nacos/test")
.accept(MediaType.APPLICATION_JSON)
.exchange()
// 验证状态
.expectStatus().isOk()
// 验证结果,注意结果是字符串格式
.expectBody(String.class).consumeWith(result -> assertTrue(result.getResponseBody().contains("hello")));
}
}
请确保nacos-provider应用已经启动,再运行上面创建的GatewayTest.java,得到结果如下,测试通过,spring-cloud-gateway的功能符合预期,成功的将请求转发到nacos-provider应用,并且成功收到响应:
至此,《Spring Cloud Gateway实战》系列的准备工作已经完成,而且开发了一个简单的应用体验最基本的Spring Cloud Gateway功能,接下来的文章,咱们一起实战更多基本功能。