本文主要对Spring Cloud Gateway进行简单的概念介绍,并通过多模块编程的方式进行一个简单的实操。
网关是一种网络设备,它用于连接不同网络之间的数据传输。它是在网络层级上工作的,能够将数据从一个网络传输到另一个网络。网关可以是物理设备,如路由器,也可以是软件程序,如网络服务器
。
在微服务架构中,网关的作用主要是用来作为客户端到内部服务端之间中间转发人,因为在微服务架构中,后端服务基本上会被放在私网中,不对外开放地址的
,那么客户端身在远方不跟后端服务在一个私网中,是无法通过具体地址访问到后端服务的。
那么这个时候,就需要一个中间人作为连接二者的点,而这个中间人,在私网中,唯独他的IP对外开放,服务器多的情况下,这个中间人还需要额外做一份请求分发
的工作,这个中间人我们专业一点就叫做:网关
问:这个时候可能会有朋友会问:客户端和服务端沟通不是可以通过OpenFeign+LoadBalancer吗????
答:现在的项目前后端分离项目比较多,我们后端是Java,OpenFeign+LoadBalancer的使用需要Jar包,但是我们前端(Vue、三剑客)语言并不能适配Jar包,所以就需要第三个人作为中间人,衔接前端客户端和后端服务端,而OpenFeign+LoadBalancer则作为后端中的通信使用,或者你可以理解访问过程为:前端客户端->网关->后端客户端->后端服务端
答:在微服务架构中,你要部署很多个后端且独立存在,这就意味着我前脚在这台服务器上刚登录认证过,后脚我的请求到另一台服务器就被认为是未认证请求,这不就成了我的每一次请求都被提示未登录认证。而我们的网关可以统一认证处理,就实现一次登录,各服务端生效的效果,当然还不止这些效果,接下来便会逐一介绍。
总而言之,网关是一个关键的网络设备,它起到连接不同网络之间的桥梁作用,使得数据能够在不同网络之间传输和交换。
下面我们通过多模块编程的方式演示Spring Cloud Gateway的使用,跳过创建过程,直接展示结果:
<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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>Spring-Cloud-Gateway-DemoartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>Spring-Cloud-Gateway-Demoname>
<description>Spring-Cloud-Gateway-Demodescription>
<packaging>pompackaging>
<modules>
<module>UserServicemodule>
<module>GatewayServicemodule>
modules>
<properties>
<java.version>17java.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<spring-boot.version>3.0.2spring-boot.version>
<spring-cloud.version>2022.0.0spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring-boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>17source>
<target>17target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>${spring-boot.version}version>
<executions>
<execution>
<id>repackageid>
<goals>
<goal>repackagegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
project>
<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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>UserServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>UserServicename>
<description>UserServicedescription>
<parent>
<groupId>com.examplegroupId>
<artifactId>Spring-Cloud-Gateway-DemoartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>17source>
<target>17target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>${spring-boot.version}version>
<configuration>
<mainClass>com.example.userservice.UserServiceApplicationmainClass>
<skip>trueskip>
configuration>
<executions>
<execution>
<id>repackageid>
<goals>
<goal>repackagegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
project>
application.yml
server:
port: 10086
controller代码
@RestController
@RequestMapping("/user")
public class UserControler {
@RequestMapping("/getrandom")
public String getRandom(){
return "userservice:10086-"+new Random().nextInt(1000);
}
}
Spring Cloud Gateway分为两个步骤:
<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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>GatewayServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>GatewayServicename>
<description>GatewayServicedescription>
<parent>
<groupId>com.examplegroupId>
<artifactId>Spring-Cloud-Gateway-DemoartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>17source>
<target>17target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>${spring-boot.version}version>
<configuration>
<mainClass>com.example.gatewayservice.GatewayServiceApplicationmainClass>
<skip>trueskip>
configuration>
<executions>
<execution>
<id>repackageid>
<goals>
<goal>repackagegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
project>
application.yml
server:
port: 8080
spring:
cloud:
gateway:
routes:
- id: userservice #服务ID
uri: http://localhost:10086 #目的地址路由
predicates: #断言
- Path=/user/** #断言匹配规则
启动UserService和启动GatewayService
我们先正常访问UserService中的路由(http://localhost:10086/user/getrandom)
结果:
现在已经确认UserService是正常的,那么我们接下来就看看通过网关(http://localhost:8080/user/getrandom)能不能访问到它:
从中可以发现,我们的Gateway可以帮我们去完成请求转发到适合的服务端上。
如果是对于多组的路由该如何编写GatewayService的application.yml呢?编写时又该注意什么?
以下面为例进行说明:
server:
port: 8080
spring:
cloud:
gateway:
routes:
- id: userservice
uri: http://localhost:10086 #路由
predicates:
- Path=/user/**, /update/**
- id: adminservice
uri: http://localhost:9000
predicates:
- Path=/admin/**
照虎画猫,我们在创建adminservice子模块与/update/xxx服务
@RestController
@RequestMapping("/update")
public class UpdateController {
@RequestMapping("/op")
public String updateOrInsert(){
return "updateOrInsert";
}
}
@RestController
@RequestMapping("/admin")
public class AdminController {
@RequestMapping("/getroles")
public String getRoles(){
return "AdminService:9000-admin";
}
}
下面就进行访问测试,网关能否正确应对多个请求的分发
http://localhost:8080/update/op (√)
http://localhost:8080/admin/getroles(√)
补充:
上面我们介绍了application.yml文件中对于数组的写法,而对于application.propeties编写方式如下server.port=9000 spring.cloud.gateway.routes[0].id=userservice spring.cloud.gateway.routes[0].uri=http://localhost:10086 spring.cloud.gateway.routes[0].predicates[0]=Path=/user/** spring.cloud.gateway.routes[0].predicates[1]=Path=/update/** spring.cloud.gateway.routes[1].id=adminservice spring.cloud.gateway.routes[1].uri=http://localhost:9000 spring.cloud.gateway.routes[1].predicates[0]=Path=/admin/**
细心的朋友可能已经发现了,在多模块项目中,我的SpringWeb依赖没有放到父模块pom中,而是放在子模块。
这是因为父类中添加SpringWeb依赖会导致全子类都使用,而我们的Spring Cloud Gateway是基于Reactive进行开发的,不能使用简单的Spring Web依赖,需要使用Spring Reactive Web依赖,但是Spring Cloud Gateway底层中已经包含了这个依赖,所以我们没有在GatewayService中导入Spring Reactive Web,在其他子模块中导入Spring web依赖的原因。
假如我们错误的将Spring Web适用到Spring Cloud Gateway模块中,也会有error抛出,示例如下:
错误中也告诉我们解决方式,要么在gateway中使用Spring Reactive Web要么吧Spring Web移除
Spring Cloud Gateway支持的断言类型目前有12种,包含以下这些:
官方参考文档:
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories
其中Cookie和Header方式的匹配需要配合正则表达式来使用
正则表达式
\w+
:匹配一个或多个字母、数字、下划线字符\s+
:匹配一个或多个空白字符 (包括空格、制表符、换行符等)。.*
:匹配任意字符(除了换行符) 零次或多次.+
:匹配除了换行符之外的任意字符一次或多次。[abc]
:匹配字符集中的任意一个字符,例如 [abc] 可以匹配a、b或c。[^abc]
:匹配除了字符集中的任意字符之外的任意字符,例如[^abc] 可以匹配除了a、b和c之外的任意字符^
:匹配字符串的开始位置$
:匹配字符串的结束位置|
:表示"或”的意思,用于匹配多个模式中的任意一个10.(): 用10.()
:于分组,可以将一组字符作为一个整体进行匹配。