搭建基本框架
项目结构
创建父项目
作用
创建一个父项目(Maven项目,可以把src
文件夹删除掉),其作用是用于管理依赖的版本号以及子项目模块
创建过程
<?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项目),用于管理公共依赖、工具类以及实体类等
创建过程
<?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>
创建订单项目
作用
创建一个订单项目模块,模拟真实业务,用于测试
创建过程
<?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>
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
package com.ledao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author LeDao
* @company
* @create 2022-04-11 23:19
*/
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
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;
/**
* @author LeDao
* @company
* @create 2022-04-11 23:17
*/
@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;
}
}
创建库存项目
作用
创建一个库存项目模块,模拟真实业务,用于测试
创建过程
<?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>
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
package com.ledao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author LeDao
* @company
* @create 2022-04-11 23:22
*/
@SpringBootApplication
public class StockApplication {
public static void main(String[] args) {
SpringApplication.run(StockApplication.class, args);
}
}
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;
/**
* @author LeDao
* @company
* @create 2022-04-11 23:23
*/
@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
访问,返回:
{"模块名称":"订单模块","请求时间":"2022-04-14 11:25:51"}
通过http://localhost:8082/stock/test
访问,返回:
{"模块名称":"库存模块","请求时间":"2022-04-14 11:25:49"}
出现上面结果说明搭建成功
开始使用Gateway
实现访问其它项目接口
<?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>
id
:路由id,设置为被访问项目的名称
uri
:路由地址,设置为被访问项目的端口
predicates
:断言规则,设置为被访问项目的请求路径
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/**
package com.ledao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author LeDao
* @company
* @create 2022-04-11 23:35
*/
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
启动Gateway网关项目
通过http://localhost:8080/order/test
访问,返回:
{"模块名称":"订单模块","请求时间":"2022-04-14 11:25:51"}
通过http://localhost:8080/stock/test
访问,返回:
{"模块名称":"库存模块","请求时间":"2022-04-14 11:25:49"}
出现上面结果说明已经成功,其中8080
为Gateway网关项目的端口
路由匹配规则
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]
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]
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]
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+
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+
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
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
一些匹配示例说明如下:
/order/**
:以/order/
开头的请求路径/order/my/{aa}
:/order/my/test
可以通过,/order/test
和/order/my/ss/test
不可以通过
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/**
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
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
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
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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QCa33qN3-1650180001231)(C:\Users\LeDao\AppData\Roaming\Typora\typora-user-images\image-20220417122637663.png)]
AddResponseHeader
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
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;
/**
* @author LeDao
* @company
* @create 2022-04-17 13:01
*/
public class MyGatewayFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("我的自定义网关过滤器");
//获取请求的参数name的值
System.out.println(exchange.getRequest().getQueryParams().get("name"));
return chain.filter(exchange);
}
@Override
public int getOrder() {
//越小优先级越高
return -1;
}
}
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;
/**
* @author LeDao
* @company
* @create 2022-04-17 13:05
*/
@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
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;
/**
* @author LeDao
* @company
* @create 2022-04-17 13:50
*/
@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限流
<!-- spring boot redis 缓存引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce pool 缓存连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
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;
/**
* @author LeDao
* @company
* @create 2022-04-17 14:36
*/
@Configuration
public class KeyResolverConfig {
@Bean
public KeyResolver pathKeyResolver() {
/*return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getURI().getPath());
}
};*/
//URL限流,上面注释代码的lambda写法
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}
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 # 每次请求消耗1个
key-resolver: "#{@pathKeyResolver}"
参数限流
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;
/**
* @author LeDao
* @company
* @create 2022-04-17 14:36
*/
@Configuration
public class KeyResolverConfig {
/**
* 参数限流
*
* @return
*/
@Bean
public KeyResolver parameterKeyResolver() {
return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("token")));
}
}
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 # 每次请求消耗1个
key-resolver: "#{@parameterKeyResolver}"
IP限流
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;
/**
* @author LeDao
* @company
* @create 2022-04-17 14:36
*/
@Configuration
public class KeyResolverConfig {
/**
* IP限流
*
* @return
*/
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName());
}
}
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 # 每次请求消耗1个
key-resolver: "#{@ipKeyResolver}"
PS.
官方文档地址:[Spring Cloud Gateway](