简单网关演示

Spring Gateway不是一个软件,而是一个依赖,所以我们要使用它需要创建一个Spring Boot项目

这个项目也要注册到注册中心,因为网关项目也是微服务项目的一部分

beijing和shanghai的项目,这个就是简单的Spring Boot项目也需要注册到注册中心,但是只需要控制层即可

创建gateway项目

gateway项目就是网关项目,需要添加配置

pom.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?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>
<parent>
<groupId>cn.tedu</groupId>
<artifactId>csmall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway</name>
<description>Demo project for Spring Boot</description>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--添加nacos依赖-->
<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>
<!--添加gateway网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

application.yml配置

1
2
3
4
5
6
7
8
9
10
11
server:
port: 10000

spring:
application:
name: gateway-server

cloud:
nacos:
discovery:
server-addr: localhost:8848

创建beijing项目

接下来创建beijing项目

pom文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?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>
<parent>
<groupId>cn.tedu</groupId>
<artifactId>csmall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>gateway-beijng</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway-beijng</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--添加spring-web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--添加nacos依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>

application.yml文件

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 9001

spring:
application:
name: beijing

cloud:
nacos:
discovery:
server-addr: localhost:8848

创建controller层,提供访问以及返回的信息

1
2
3
4
5
6
7
8
@RestController
@RequestMapping("/bj")
public class BeiJingController {
@GetMapping("/show")
public String show(){
return "这里是北京~";
}
}

创建shanghai项目

接下来创建shanghai项目

pom文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?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>
<parent>
<groupId>cn.tedu</groupId>
<artifactId>csmall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>gateway-beijng</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway-shanghai</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--添加spring-web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--添加nacos依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>

application.yml文件

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 9002

spring:
application:
name: shanghai

cloud:
nacos:
discovery:
server-addr: localhost:8848

创建controller层,提供访问以及返回的信息

1
2
3
4
5
6
7
8
@RestController
@RequestMapping("/sh")
public class BeiJingController {
@GetMapping("/show")
public String show(){
return "这里是上海~";
}
}

通过gateway项目访问其他服务

我们想要实现的目标是

通过10000端口访问beijing和shanghai这两个服务

从而实现gateway成为项目的统一入口的效果,需要添加SpringMVC的依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

配置application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server:
port: 10000

spring:
application:
name: gateway-server

cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes: #开始编写路由信息,routes是一个数组类型的变量,yml文件中出现”- ...“表示当前配置的是数组的元素
# 如果想要访问下面数组中的uri属性,实际上的结构是:spring.cloud.gateway.routes[0].uri
- id: beijing # 设置这个路由的名称,名称和项目没有任何关系,只是别和其他项目名称重复
uri: lb://beijing # 设置路由的目标,lb就是loadBalance的缩写,beijing是beijing项目注册到nacos的名称
predicates: #predicates翻译成断言,断言的意思就是判断,某个条件为真,才能做想做的事情
- Path=/bj/** # Path会设置当前访问10000端口时如果路径为bj开头,则会路由到上面的uri
#实际上访问路径为 localhost:10000/bj/show
- id: shanghai
uri: lb://shanghai
predicates:
- Path=/sh/**

测试

首先启动nacos服务

然后顺序启动beijing、shanghai、gateway服务

访问路径为: http://localhost:10000/bj/show

http://localhost:10000/sh/show

我们在启动gateway项目时,报错,提示以下内容

1
2
3
4
5
6
7
Description:

Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway.

Action:

Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency.

在同一个项目中引入Spring MVC和Gateway的依赖,默认情况下启动会报错

原因是Spring MVC框架中自带一个Tomcat服务器

而Gateway框架中自带一个Netty的服务器

在项目启动时,两个框架中包含的服务器都想占用相同的端口,因为争夺端口号和主动权而发生冲突,导致服务启动异常

Action中给出了解决方案,可以通过spring.main.web-application-type=reactive启动netty服务器,或者移除web依赖

gateway内置断言

除了使用的Path

还有很多其他的断言,比如

after

before

between

cookie

header

host

method

query

remoteaddr

时间相关的断言

afterbeforebetween

判断当前时间在指定时间之后、之前、之间的操作

如果条件满足可以执行路由操作,否则拒绝访问

利用代码获得当前时间格式

1
ZonedDateTime.now()

时间格式可能是这样

1
2023-01-04T21:16:42.213+08:00[Asia/Shanghai]

使用after断言,在指定时间之后才能访问

1
2
3
predicates:
- Path=/sh/**
- After=2023-01-04T21:22:42.213+08:00[Asia/Shanghai]

使用before断言,指定时间之前才能访问

1
- Before=2023-01-04T21:16:42.213+08:00[Asia/Shanghai]

使用between断言,在指定时间之间才能访问

1
- Between=2023-01-04T21:16:42.213+08:00[Asia/Shanghai],2023-01-04T21:24:42.213+08:00[Asia/Shanghai]

要求指定请求参数

query断言,判断是否包含指定的参数名称,包含指定参数名称才能通过路由

1
- Query=name #检查请求中是否包含name参数(可以没有值),包含才能路由

内置过滤器

Gateway还提供了内置的过滤器

内置过滤器允许我们在路由请求到目标的同时,对这个请求进行一些加工或处理

演示AddRequestParameter过滤器,他的作用在请求中添加参数

1
2
3
4
5
filters:
- AddRequestParameter=age,80
predicates:
- Path=/sh/**
- Query=name #检查请求中是否包含name参数(可以没有值),包含才能路由

在shanghai的控制器方法中添加接收name和age参数的代码

1
2
3
4
5
6
7
8
9
@RestController
@RequestMapping("/sh")
public class ShangHaiController {
@GetMapping("/show")
public String show(String name,Integer age){
System.out.println(ZonedDateTime.now());
return "这里是上海~"+name+","+age;
}
}

动态路由

网关项目随着微服务数量的增多,gateway项目的yml文件配置会越来越多,维护的工作量也会越来越大

所以我们希望gateway能够自动路由到每个模块,也就是有默认的路由规则生成

这样的话,我们不管当前项目有多少个路由目标,都不需要维护yml文件了

这个就是Spring Gateway的动态路由功能,可以通过以下配置开启

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
discovery:
locator:
# 默认情况下gateway不开启动态路由,通过enabled=true开启动态路由
# 路由规则是10000端口后面先编写路由目标注册到注册中心的名称,再编写具体路径
enabled: true

注意:gateway项目要从注册中心中获得服务名称,所以一定要在gateway服务启动之前就保证注册中心中的服务列表完整,意思就是gateway服务最后启动