前言:
gateway 组件是SpringCloud 组件中的网关组件,主要是解决路由转发的问题;跟nginx有点类似,区别是nginx多用在前端上,gateway用在后端上。当然gateway的功能不止路由转发,还可以用来:
1.针对所有请求做统一鉴权、限流、熔断、日志;
2.协议转化,针对后端多种协议可以在网关层统一处理后以http对外服务;
3.统一错误代码处理(跟springboot统一错误处理配置一样);
一、配置说明
gateway的配置文件是其使用的核心。
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Path=/table-service/**
filters:
- StripPrefix=1
字段说明:
id:自定义路由的ID;
uri:目标服务器地址,同时支持 URI(http://ip:port/route) 和 lb(lb://应用注册服务名) 方式,推荐使用lb方式;
predicates:路由条件,根据匹配结果决定是否执行该请求路由;
filters:过滤规则,包含 pre 和 post 过滤,其中 StripPrefix=1 表示根据请求时去掉URL路径的第一个前缀。
二、路由条件配置说明
即 predicates 配置项,支持多种规则:
1. 指定时间规则匹配路由
时间的书写必须是ZoneDateTime格式。
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- After=2022-07-20T10:25:00.000+08:00[Asia/Shanghai]
有三种规则:
After:指定时间之后转发到该服务;
Before:指定时间之前转发到该服务;
Between:两个时间之前转发到该服务,两个时间之间用逗号隔开;
2. Cookie 匹配路由
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Cookie=cho,123
上述配置表示请求时 Cookie 必须携带 cho=123 键值对才能转发到该服务;逗号前表示键值,逗号后表示该键对应的值,该值是个正则表达式。
3. Header 匹配路由
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Header=cho,123
上述配置表示请求时 header 必须携带 cho=123 键值对才能转发到该服务;逗号前表示键值,逗号后表示该键对应的值,该值是个正则表达式。
4. Host 匹配路由
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Host=127.0.0.1,localhost
上述配置表示请求时 Host 必须携带 127.0.0.1,localhost 中的任何一个值才能转发到该服务;匹配多值时用逗号隔开,并且支持星号(*)做域名通配符。
5. 请求方法匹配路由
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Method=POST,GET
上述配置表示请求时只有 GET 和 POST 方法才能转发到该服务;匹配多值时用逗号隔开。
6. 请求路径匹配路由
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Path=/table/**
该方式是使用最多的。/* 表示单层路径匹配,/** 表示多层路径匹配。
三、过滤配置说明
即 filters 配置项,支持多种配置:
1. AddRequestParameters GatewayFilter Factory
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Path=/table/**
filters:
- AddRequestParameter=foo,bar
上述配置会对走该路由的所有请求都加上 foo=bar 参数。
2. AddResponseHeader GatewayFilter Factory
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Path=/table/**
filters:
- AddResponseHeader=foo,bar
上述配置会对走该路由的所有请求的返回都加上响应头 foo=bar 。
3. RequestRateLimiter GatewayFilter Factory
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Path=/table/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
这个过滤器是基于令牌桶实现的,replenishRate 表示令牌填充速度,burstCapacity 表示令牌桶容量;限流用的,需要集成redis才行。限流的使用略复杂,建议单独研究。
4. Retry GatewayFilter Factory
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Path=/table/**
filters:
- name: Retry
args:
retries: 3
status: 503
总共涉及4个参数:
retries:重试次数;
status:http 请求返回的状态码,上述配置表示服务端返回503时发起重试;
methods:指定请求类型才会发起重试;
series:配置错误码段,表示符合状态码才发起重试,默认SERVER_ERROR(5),即 5xx 段状态码才会发起重试;如果 series 配置了错误码,但 status 缺省,则仍然匹配 series 进行重试。
四、自定义 Filter
自定义 filter 有两个,GlobalFilter 和 GatewayFilter ;GlobalFilter 对全局生效,GatewayFilter 只对配置了的才生效。
1. 自定义 GlobalFilter
可以存在多个 GlobalFilter,执行顺序由 getOrder() 控制,值越小执行越靠前。
@Slf4j
@Service
public class GatewayGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("pre action..."); //请求到路由前的操作...
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("post action..."); //请求返回后的操作...
}));
}
@Override
public int getOrder() {
return 0;
}
}
2. 自定义 gatewayFilter
@Slf4j
@Service
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.CustomConfig> {
public CustomGatewayFilterFactory() {
super(CustomConfig.class);
}
@Override
public GatewayFilter apply(CustomConfig config) {
return ((exchange, chain) -> {
String name = config.getName();
log.info("config name:{}", name);
log.info("pre action...");
if (name.equals("123")) { // 使用config值的样例
return exchange.getResponse().setComplete(); // 不返回任何信息
}
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("post action...");
}));
});
}
public static class CustomConfig {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
上述对应的配置文件:
spring:
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Path=/table/**
filters:
- name: Custom
args:
name: 123
几点注意:
1. 类名一般遵循以 GatewayFilterFactory 结尾,所以默认情况下过滤器 name 会采用该自定义类名的前缀,这里配置 name=Custom;或者类名完全不遵循上述规则,,配置时name为类全名;
2. 在 apply 方法中,同时包含 Pre 和 Post 过滤;在 then 方法中是请求执行结束之后的处置;
3. CustomConfig 是一个配置类,其属性值可在配置文件 args 下一层进行配置;
3. 补充
以下是请求不满足条件时的 json 返回。
// msg 是个 json 字符串
DataBuffer bodyDataBuffer =response.bufferFactory().wrap(msg.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(bodyDataBuffer));
五、整个 demo
1. 本地起一个eureka的注册中心(也可以用nacos等其他的注册中心)
端口:9099
本片代码不是该博客核心,忽略。
2. 起一个gateway的client注册到eureka
依赖:
<?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>org.example</groupId>
<artifactId>eureka-gateway</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
<dependencies>
<!--在springboot的pom文件中,该依赖已经集成了springMVC等web启动器,不需要再添加spring-boot-starter-web 依赖了-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
启动类:
@EnableEurekaClient
@SpringBootApplication
public class GatewayServer {
public static void main(String[] args) {
SpringApplication.run(GatewayServer.class, args);
}
}
配置文件:
server:
port: 8999
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: table-service
uri: lb://table-service
predicates:
- Path=/table-service/**
filters:
- StripPrefix=1
- id: table-service
uri: lb:ws://table-service #这是websocket转发的配置
predicates:
- Path=/table-socket/**
filters:
- StripPrefix=1
eureka:
instance:
lease-expiration-duration-in-seconds: 10
lease-renewal-interval-in-seconds: 5
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
service-url:
defaultZone: http://user:passwd@localhost:9099/eureka/