Spring Cloud Gateway 支持两种不同的用法:
- 编码式
- properties、yml 配置
编码式
首先创建 Spring Boot 项目,添加 Spring Cloud Gateway 模块:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
项目创建成功后,直接配置一个 RouteLocator 这样一个 Bean,就可以实现请求转发。
@Bean
RouteLocator routeLocator(RouteLocatorBuilder builder){
return builder.routes().
route("gongjie",j -> j.path("/get").uri("http://httpbin.org"))
.build();
}
这里只需要提供 RouteLocator 这个 Bean,就可以实现请求转发。配置完成后,重启项目,访问:http://localhost:8080/get
配置
properties配置
spring.cloud.gateway.routes[0].id=gongjie
spring.cloud.gateway.routes[0].uri=http://httpbin.org
spring.cloud.gateway.routes[0].predicates[0]=Path=/get
yml配置
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Path=/get
服务化
首先给 Gateway 添加依赖,将之注册到 Eureka 上。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
修改配置
spring:
cloud:
gateway:
discovery:
locator:
enabled: true #开启自动代理
application:
name: gateway
eureka:
client:
service-url:
defaultZone: http://localhost:1999/eureka
调用:http://localhost:8080/APPUSER/api/user/getUserInfo/1
APPUSER是另外一个服务的服务名称。/api/user/getUserInfo是接口地址。
Predicate
spring cloud gateway 通过谓词(Predicate)来匹配来自用户的请求
- After 通过时间匹配:
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- After=2021-09-01T01:01:01+08:00[Asia/Shanghai]
表示,请求时间在 2021-09-01T01:01:01+08:00[Asia/Shanghai] 时间之后,才会被路由。
除了 After 之外,还有两个关键字:
Before,表示在某个时间点之前进行请求转发
Between,表示在两个时间点之间,两个时间点用 , 隔开
- Method 通过请求方式匹配:
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Method=GET
这个配置表示只给 GET 请求进行路由
- Path 通过请求路径匹配:(重点)
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Path=/get
表示路径满足 get这个规则,都会被进行转发到http://httpbin.org/get
比如:http://localhost:8080/get
- Query 通过参数进行匹配:
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Query=name
表示请求中一定要有 name 参数才会进行转发,否则不会进行转发。
也可以指定参数和参数的值。
例如参数的 key 为 name,value 必须要以 java 开始
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Query=name,java.*
- Header 通过请求头匹配
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Header=requestId
表示请求头中一定要有 requestId 参数才会进行转发,否则不会进行转发。也可以指定参数和参数的值。
例如参数的 key 为 requestId,value 必须要为数字
- RemoteAddr 通过ip地址匹配
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- RemoteAddr=47.105.198.54/24
- host 通过host匹配
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Host=**.gongjie.top
- Cookie 通过Cookie匹配
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Cookie=CookieName, .*jie.*
这里判断cookie的 CookieName是否包含"jie" 是的话就路由到对应的服务上去
- 组合使用
当它们同时存在于同一个路由时,请求必须同时满足所有的谓词条件才被这个路由匹配。
注意:一个请求满足多个路由的谓词条件时,请求只会被首个成功匹配的路由转发
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: http://httpbin.org
predicates:
- Query=name,java.*
- Method=GET
- After=2021-01-01T01:01:01+08:00[Asia/Shanghai]
Filter
Spring Cloud Gateway 中的过滤器分为两大类:
- GatewayFilter:应用到单个路由或者一个分组的路由上。
-
GlobalFilter:应用到所有的路由上。
image.png
AddRequestParameter 过滤器使用:
添加请求参数的过滤器
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: lb://APPUSER
filters:
- AddRequestParameter=name,gong
predicates:
- Cookie=CookieName, .*jie.*
自动带上了参数name并且值为gong。
如果你有多个服务需要进行转发,配置多个routes规则就行
spring:
cloud:
gateway:
routes:
- id: gongjie
uri: lb://APPUSER
filters:
- AddRequestParameter=name,gong
predicates:
- Cookie=CookieName, .*jie.*
- id: yuanj
uri: lb://APPPAY
filters:
- AddRequestParameter=name,Pay
predicates:
- Method=GET
一个转发到APPUSER,一个转发到APPPAY。
自定义GatewayFilter
/**
* 统计某个或者某种路由的处理时长
*/
public class customGatewayFilter implements GatewayFilter, Ordered {
private static final Logger log = LoggerFactory
.getLogger( customGatewayFilter.class );
private static final String COUNT_START_TIME = "countStartTime";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//得到当前时间
Instant now = Instant.now();
//Instant.now()使用的是UTC时间,会与北京时间相差八小时
now.plusMillis(TimeUnit.HOURS.toMillis(8));
//now.toEpochMilli() 毫秒数
exchange.getAttributes().put(COUNT_START_TIME, now.toEpochMilli());
return chain.filter(exchange).then(
Mono.fromRunnable(()->{
long startTime = exchange.getAttribute(COUNT_START_TIME);
long endTime = (Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8)).toEpochMilli() - startTime);
log.info(exchange.getRequest().getURI().getRawPath() + ": " + endTime + "ms");
})
);
}
@Override
public int getOrder() {
return 0;
}
}
上述代码中,getOrder()方法是来给过滤器设定优先级别的,值越大则优先级越低。需要将自定义的GatewayFilter 注册到router中,代码如下:
@Bean
RouteLocator routeLocator(RouteLocatorBuilder builder){
return builder.routes()
.route("customGongj",
j -> j.path("/get").filters(f -> f.filter(new CustomGatewayFilter())
.addRequestHeader("auth","gongjie")).uri("http://httpbin.org")
)
.build();
}
使用psotman测试
控制台打印
2020-09-13 15:00:28.691 INFO 10364 --- [ctor-http-nio-4] com.gongj.gateway.CustomGatewayFilter : /get: 28801395ms
自定义过滤器工厂
自定义GatewayFilter又有两种实现方式,一种是上面的直接实现GatewayFilter接口,另一种是自定义过滤器工厂(继承AbstractGatewayFilterFactory类) , 选择自定义过滤器工厂的方式,可以在配置文件中配置过滤器了。
@Component
public class CustomerGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomerGatewayFilterFactory.Config> {
private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilterFactory.class );
private static final String COUNT_START_TIME = "countStartTime";
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
if (!config.isEnabled()) {
return chain.filter(exchange);
}
exchange.getAttributes().put(COUNT_START_TIME, System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute(COUNT_START_TIME);
if (startTime != null) {
StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath())
.append(": ")
.append(System.currentTimeMillis() - startTime)
.append("ms");
sb.append(" params:").append(exchange.getRequest().getQueryParams());
log.info(sb.toString());
}
})
);
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("enabled");
}
public CustomerGatewayFilterFactory() {
super(Config.class);
log.info("Loaded GatewayFilterFactory [CustomerGatewayFilterFactory]");
}
public static class Config {
/**
* 控制是否开启统计
*/
private boolean enabled;
public Config() {}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
application.yml 中 网关路由配置如下:
spring:
cloud:
gateway:
routes:
- id: customer_route
uri: http://httpbin.org
filters:
- Customer=true
- AddRequestHeader=auth,gongjie123
predicates:
- Method=GET
postman测试
控制台打印:
2020-09-13 15:19:16.233 INFO 12836 --- [ctor-http-nio-3] c.g.g.CustomerGatewayFilterFactory : /get: 437ms params:{}
Global filter
Spring Cloud Gateway框架内置的GlobalFilter如下:
内置的 GlobalFilter 能够满足大多数的需求了,但是如果遇到特殊情况,内置满足不了我们的需求,还可以自定义GlobalFilter。
自定义GlobalFilter
下面的我们自定义一个GlobalFilter,去校验所有请求的请求参数中是否包含“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。
/**
* Token 校验全局过滤器
*/
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger( AuthorizeFilter.class );
private static final String AUTHORIZE_TOKEN = "token";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst( AUTHORIZE_TOKEN );
if ( StringUtils.isBlank( token )) {
log.info( "token is empty ..." );
exchange.getResponse().setStatusCode( HttpStatus.UNAUTHORIZED );
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
执行不带token的请求
控制台打印
2020-09-13 15:26:44.540 INFO 4608 --- [ctor-http-nio-3] com.gongj.gateway.AuthorizeFilter : token is empty ...
执行带token的请求
控制台打印
2020-09-13 15:28:59.125 INFO 4608 --- [ctor-http-nio-4] c.g.g.CustomerGatewayFilterFactory : /get: 533ms params:{token=[gggg]}
代码参考:https://blog.51cto.com/13698036/2395413
大佬江南一点雨
个人博客:http://www.javaboy.org/
必读的官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html