搭建基本框架
项目结构
创建父项目
作用
创建一个父项目(Maven项目,可以把src
文件夹删除掉),其作用是用于管理依赖的版本号以及子项目模块
创建过程
pom.xml
的内容如下:(modules
节点的内容不用管,这是由IntelliJ IDEA自动生成用于管理子项目模块的)
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 55 56 57 58
| <?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"> <modelVersion>4.0.0</modelVersion>
<groupId>com.ledao</groupId> <artifactId>GatewayDemo</artifactId> <version>1.0-SNAPSHOT</version> <modules> <module>order</module> <module>stock</module> <module>gateway</module> <module>common</module> </modules> <packaging>pom</packaging>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> <springboot.version>2.3.2.RELEASE</springboot.version> <springcloudalibaba.version>2.2.6.RELEASE</springcloudalibaba.version> <common.version>1.0-SNAPSHOT</common.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${springboot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${springcloudalibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.ledao</groupId> <artifactId>common</artifactId> <version>${common.version}</version> </dependency> </dependencies> </dependencyManagement> </project>
|
创建公共项目
作用
创建一个公共项目(Maven项目),用于管理公共依赖、工具类以及实体类等
创建过程
pom.xml
的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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>GatewayDemo</artifactId> <groupId>com.ledao</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>common</artifactId>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
|
创建订单项目
作用
创建一个订单项目模块,模拟真实业务,用于测试
创建过程
pom.xml
的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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>GatewayDemo</artifactId> <groupId>com.ledao</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>order</artifactId>
<dependencies> <dependency> <groupId>com.ledao</groupId> <artifactId>common</artifactId> </dependency> </dependencies> </project>
|
application.yml
配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| server: port: 8081 servlet: context-path: / tomcat: uri-encoding: utf-8
spring: application: name: order jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss
|
新建启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.ledao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication public class OrderApplication {
public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
|
控制层,通过http://localhost:8081/order/test
访问
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
| package com.ledao.controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.Date; import java.util.HashMap; import java.util.Map;
@RestController @RequestMapping("/order") public class OrderController {
@RequestMapping("/test") public Map<String, Object> test() { Map<String, Object> resultMap = new HashMap<>(16); resultMap.put("模块名称", "订单模块"); resultMap.put("请求时间", new Date()); return resultMap; } }
|
创建库存项目
作用
创建一个库存项目模块,模拟真实业务,用于测试
创建过程
pom.xml
的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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>GatewayDemo</artifactId> <groupId>com.ledao</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>stock</artifactId>
<dependencies> <dependency> <groupId>com.ledao</groupId> <artifactId>common</artifactId> </dependency> </dependencies> </project>
|
application.yml
配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| server: port: 8082 servlet: context-path: / tomcat: uri-encoding: utf-8
spring: application: name: stock jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss
|
新建启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.ledao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication public class StockApplication {
public static void main(String[] args) { SpringApplication.run(StockApplication.class, args); } }
|
控制层,通过http://localhost:8082/stock/test
访问
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
| package com.ledao.controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.Date; import java.util.HashMap; import java.util.Map;
@RestController @RequestMapping("/stock") public class StockController {
@RequestMapping("/test") public Map<String, Object> test() { Map<String, Object> resultMap = new HashMap<>(16); resultMap.put("模块名称", "库存模块"); resultMap.put("请求时间", new Date()); return resultMap; } }
|
测试
启动订单项目和库存项目
通过http://localhost:8081/order/test
访问,返回:
1
| {"模块名称":"订单模块","请求时间":"2022-04-14 11:25:51"}
|
通过http://localhost:8082/stock/test
访问,返回:
1
| {"模块名称":"库存模块","请求时间":"2022-04-14 11:25:49"}
|
出现上面结果说明搭建成功
开始使用Gateway
实现访问其它项目接口
首先创建Gateway项目模块,pom.xml
内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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>GatewayDemo</artifactId> <groupId>com.ledao</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>gateway</artifactId>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies> </project>
|
application.yml
配置文件如下,使用spring.cloud.gateway.routes
配置
id
:路由id,设置为被访问项目的名称
uri
:路由地址,设置为被访问项目的端口
predicates
:断言规则,设置为被访问项目的请求路径
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
| server: port: 8080 servlet: context-path: / tomcat: uri-encoding: utf-8
spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/**
- id: stock uri: http://localhost:8082/ predicates: - Path=/stock/**
|
启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.ledao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication public class GatewayApplication {
public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
|
测试
启动Gateway网关项目
通过http://localhost:8080/order/test
访问,返回:
1
| {"模块名称":"订单模块","请求时间":"2022-04-14 11:25:51"}
|
通过http://localhost:8080/stock/test
访问,返回:
1
| {"模块名称":"库存模块","请求时间":"2022-04-14 11:25:49"}
|
出现上面结果说明已经成功,其中8080
为Gateway网关项目的端口
路由匹配规则
匹配指定日期之后的请求After
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** - After=2022-04-14T13:12:12+08:00[Asia/Shanghai]
|
匹配指定日期之前的请求Before
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** - Before=2022-04-14T11:12:00+08:00[Asia/Shanghai]
|
匹配两个指定日期之间的请求Between
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** - Between=2022-04-14T13:12:00+08:00[Asia/Shanghai],2022-04-14T14:12:00+08:00[Asia/Shanghai]
|
Cookie路由匹配规则Cookie
,下面配置中token是名称,\d+
为值的匹配规则(只包含数字,1个或多个)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** - Cookie=token, \d+
|
Header路由匹配规则Header
,下面配置中X-Request-Id是名称,\d+
的说明看上面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** - Header=X-Request-Id, \d+
|
Host路由匹配规则,规定只有指定域名才能访问接口,下面配置了mytest.com
和mytest2.com
两个域名
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** - Host=**.mytest.com,**.mytest2.com
|
Method路由匹配规则,下面只匹配GET请求,如果是POST请求就不能访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** - Method=GET
|
path路由匹配规则,根据请求路径匹配
一些匹配示例说明如下:
/order/**
:以/order/
开头的请求路径
/order/my/{aa}
:/order/my/test
可以通过,/order/test
和/order/my/ss/test
不可以通过
1 2 3 4 5 6 7 8 9 10 11 12 13
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/**
|
Query路由匹配规则,请求时需要带指定参数(值可为空),下面配置中需要带name参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** - Query=name
|
内置过滤器
AddRequestParameter
向请求接口传递参数,格式为AddRequestParameter=name,ledao
,name为参数名,ledao为值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** filters: - AddRequestParameter=name,ledao
|
RewritePath
用于实现URL重写,下面的配置会把/gateway/order/test
重写成/order/test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/gateway/** filters: - RewritePath=/gateway(?<segment>/?.*), $\{segment}
|
SetStatus
设置返回的结果状态码,下面的配置返回404,HTTP常见状态码查看:http常见状态码有哪些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** filters: - SetStatus=404
|
效果截图
返回信息添加Header头信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** filters: - AddResponseHeader=X-Response-Author,LeDao
|
结果截图
更多
查看:Spring Cloud Gateway
自定义GatewayFilter
要先把application.yml的Gateway配置删除,然后新建MyGatewayFilter类实现GatewayFilter、Ordered接口重写filter和getOrder方法
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
| package com.ledao.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono;
public class MyGatewayFilter implements GatewayFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("我的自定义网关过滤器"); System.out.println(exchange.getRequest().getQueryParams().get("name")); return chain.filter(exchange); }
@Override public int getOrder() { return -1; } }
|
新建MyGatewayFilterConfig配置类注册Bean
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
| package com.ledao.config;
import com.ledao.filter.MyGatewayFilter; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class MyGatewayFilterConfig {
@Bean public RouteLocator routeLocator(RouteLocatorBuilder builder) { return builder.routes().route(r -> r .path("/order/**") .uri("http://localhost:8081/") .filter(new MyGatewayFilter()) .id("myGatewayFilter") ).build(); } }
|
自定义GlobalFilter
新建MyGlobalFilter类实现GlobalFilter、Ordered接口重写filter和getOrder方法
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
| package com.ledao.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono;
@Component public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("我的自定义全局过滤器"); return chain.filter(exchange); }
@Override public int getOrder() { return 0; } }
|
限流
限流算法
Gateway默认给我们实现了限流实现,也就是网关拦截器RequestRateLimiter,RequestRateLimiter的底层实现是令牌桶限流算法,需要引入Redis
URL限流
引入Redis依赖
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
|
限流配置类
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
| package com.ledao.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import reactor.core.publisher.Mono;
@Configuration public class KeyResolverConfig {
@Bean public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getURI().getPath()); } }
|
application.yml
配置,把之前的MyGatewayFilterConfig
这个类的@Configuration
注解删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 redis-rate-limiter.burstCapacity: 2 redis-rate-limiter.requestedTokens: 1 key-resolver: "#{@pathKeyResolver}"
|
参数限流
限流配置类,只能存在一个限流Bean,多个会报错,请求时要带token参数(没有就无法请求)
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
| package com.ledao.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import reactor.core.publisher.Mono;
import java.util.Objects;
@Configuration public class KeyResolverConfig {
@Bean public KeyResolver parameterKeyResolver() { return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("token"))); } }
|
application.yml配置,在之前的基础上直接修改key-resolver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 redis-rate-limiter.burstCapacity: 2 redis-rate-limiter.requestedTokens: 1 key-resolver: "#{@parameterKeyResolver}"
|
IP限流
限流配置类
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
| package com.ledao.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import reactor.core.publisher.Mono;
import java.util.Objects;
@Configuration public class KeyResolverConfig {
@Bean public KeyResolver ipKeyResolver() { return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName()); } }
|
application.yml配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring: application: name: gateway jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss cloud: gateway: routes: - id: order uri: http://localhost:8081/ predicates: - Path=/order/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 redis-rate-limiter.burstCapacity: 2 redis-rate-limiter.requestedTokens: 1 key-resolver: "#{@ipKeyResolver}"
|
PS.
官方文档地址:Spring Cloud Gateway