使用 Spring cloud Gateway 构建微服务网关
云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!
使用 Spring cloud Gateway 构建微服务网关
Spring Cloud Gateway 是 Spring Cloud 的一个子项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
相关概念
- Route(路由):网关的基本构件块,类似于 nginx 的 location 配置。由一个 ID、一个目标 URI、一组 Predicate 和一组 Filter 定义
- Predicate(断言):路由组成的一部分,主要负责路由的匹配,来决定此次请求是否匹配路由,我们可以使用它匹配来自 HTTP 请求的任何内容,比如路径、参数或者 header 信息等等
- Filter(过滤器):这个是 GatewayFilter 的实例,请求经过 Predicate 匹配路由之后执行 Filter,我们可以使用它修改请求和响应。
快速上手
Spring Cloud Gateway 网关路由有两种配置方式:
- 通过配置文件配置
- 通过 @Bean 自定义 RouteLocator 去配置
这两种方式是等价的,建议使用配置文件配置。因为 Spring Cloud Gateway 使用响应式编程框架,学习曲线相对陡峭。
引入依赖:
<properties>
<java.version>1.8</java.version>
<!-- springcloud 版本 -->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<!-- 服务注册发现相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- gateway 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
Spring cloud gateway 是使用 netty+webflux 实现,因此不需要再引入 web 模块。
配置文件:
server:
port: 8088
eureka:
client:
serviceUrl:
# 注册中心地址
defaultZone: http://dev-crm-host001.ymt.io:8761/eureka
spring:
application:
name: test-gateway
cloud:
gateway:
routes:
- id: test1
uri: http://www.baidu.com
predicates:
- Path=/baidu/**
filters:
- StripPrefix=1
各字段含义如下:
- id:我们自定义的路由 ID,保持唯一
- uri:目标服务地址,大部分场景我们是转发到某个服务上,配置 uri: lb://user-service 意思是请求要转发到注册中心的 user-service 服务上。
- predicates:路由条件,接受一个参数,返回一个布尔结果决定是否匹配。Gateway 为我们内置了多种路由条件,包括 Path、Cookie、Param、Header、Before、After 等等,开箱即用,当然我们也可以自己实现 predicates
- filters:过滤规则,当请求经过 predicate 匹配成功后,执行 filter,我们可以使用它修改请求和响应,示例表示目标服务收到的 path 将无第一级。
启动程序,当我们访问 localhost:8088/baidu 时,gateway 会根据我们配置的路由规则转发到 https://www.baidu.com
路由规则:Predicate 介绍
Spring Cloud Gateway 的功能很强大,我们仅仅通过 Predicates 的设计就可以看出来,前面我们只是使用了 predicates 进行了简单的条件匹配,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。
网上有一张图总结了 Spring Cloud 内置的集中 Predicate:
说白了 Predicate 就是实现了一组匹配规则,方便让请求过来找到对应的 Route 进行处理。接下来介绍一下内置几种 Predicate 的使用。
通过时间匹配
Predicate 支持设置一个时间,在请求进行转发的时候,可以通过判断在这个时间之前或者之后进行转发。比如我们现在设置只有在 2020 年 1 月 1 日才会转发到 https://www.baidu.com/,在这之前不进行转发,我就可以这样配置:
spring:
cloud:
gateway:
routes:
- id: time_route
uri: http://www.baidu.com
predicates:
- After=2020-01-01T01:01:01+08:00[Asia/Shanghai]
Spring 是通过 ZonedDateTime 来对时间进行的对比,ZonedDateTime 是 Java 8 中日期时间功能里,用于表示带时区的日期与时间信息的类,ZonedDateTime 支持通过时区来设置时间,中国的时区是:Asia/Shanghai。
Before Predicate 刚好相反,在某个时间之前的请求的请求都进行转发。我们把上面路由规则中的 After 改为 Before,如下:
spring:
cloud:
gateway:
routes:
- id: time_route
uri: http://www.baidu.com
predicates:
- Before=2020-01-01T01:01:01+08:00[Asia/Shanghai]
就表示在这个时间之前可以进行路由,在这时间之后停止路由。
除过在时间之前或者之后外,Gateway 还支持限制路由请求在某一个时间段范围内,可以使用 Between Predicate 来实现。
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://www.baidu.com
predicates:
- Between=2019-01-01T01:01:01+08:00[Asia/Shanghai], 2020-01-01T01:01:01+08:00[Asia/Shanghai]
通过 Cookie 匹配
Cookie Route Predicate 可以接收两个参数,一个是 Cookie name,一个是正则表达式。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://www.baidu.com
predicates:
- Cookie=name, ymt
通过 Header 属性匹配
Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式。
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://www.baidu.com
predicates:
- Header=name, ymt
通过 Host 匹配
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://www.baidu.com
predicates:
- Host=**.baidu.com
通过 Request Method 匹配
可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://www.baidu.com
predicates:
- Method=GET
通过请求路径匹配
之前示例是使用的请求路径去匹配的。
spring:
cloud:
gateway:
routes:
- id: test1
uri: http://www.baidu.com
predicates:
- Path=/baidu/**
filters:
- StripPrefix=1
通过请求参数匹配
Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://www.baidu.com
predicates:
- Query=type,baidu
如上图所示,输入 localhost:8088/?type=baidu 会被转发到 https://www.baidu.com/?type=baidu
如果写成 - Query=type,这样配置,只要请求中包含 type 属性的参数即可匹配路由。
通过请求 ip 地址进行匹配
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://www.baidu.com
predicates:
- RemoteAddr=192.168.1.1/24
组合使用
以上示例演示了单个 Predicate 的使用,Predicate 也可以放在一起组合使用。
spring:
cloud:
gateway:
routes:
- id: combination_route
uri: http://ityouknow.com
predicates:
- RemoteAddr=192.168.1.1/24
- Query=type,baidu
- Path=/baidu/**
各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。
一个请求满足多个路由的 Predicate 条件时,请求只会被首个成功匹配的路由转发
如何自己实现 Predicate
以上介绍了内置的 Predicate 的使用,但是在一些特殊的场景下,内置的 Predicate 可能满足不了我们的需求,这个时候我们就可以根据 Gateway 提供的接口实现自己的 Predicate。
需求背景
系统中有一些请求需要根据参数去转发,比如根据查询参数的某一个范围去转发,例如 /server?type=A,当 type 为 A、B、C 三个其中一个值时转发到 server1 服务,type 为 D、E、F 三个其中一个值时转发到 server2 服务。我们可以通过继承 org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory 实现
/**
* 工作流相关的断言
*
* @author zhangtao
* @since 2020-01-06 7:26 下午
*/
@Component
public class TypeRoutePredicateFactory extends AbstractRoutePredicateFactory<WorkFlowRoutePredicateFactory.Config> {
public WorkFlowRoutePredicateFactory() {
super(WorkFlowRoutePredicateFactory.Config.class);
}
private static final String DATETIME_KEY = "type";
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList(DATETIME_KEY);
}
// 配置参数的类型,此时指定为List
@Override
public ShortcutType shortcutType() {
return ShortcutType.GATHER_LIST;
}
// 判断的逻辑,如果返回为true则匹配成功
@Override
public Predicate<ServerWebExchange> apply(WorkFlowRoutePredicateFactory.Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// 判断参数中是否有配置的type
List<String> types = exchange.getRequest().getQueryParams().get("type");
if (types != null && types.size() == 1 && config.getType().contains(types.get(0))) {
return true;
}
return false;
}
};
}
@Validated
public static class Config {
private List<String> type = new ArrayList<>();
public List<String> getType() {
return type;
}
public void setType(List<String> type) {
this.type = type;
}
}
}
配置文件:
spring:
cloud:
gateway:
routes:
- id: server1-route
uri: http://www.baidu.com
predicates:
# Type为,TypeRoutePredicateFactory类 RoutePredicateFactory前面的值
- Type=A,B,C
- id: server2-route
uri: https://cn.bing.com/
predicates:
# Type为,TypeRoutePredicateFactory类 RoutePredicateFactory前面的值
- Type=D,E,F
以上配置为,当请求参数中 type 的值为 A、B、C 其中一个时则匹配成功,通过 server1-route 路由转发到 http://www.baidu.com,当请求参数中 type 的值为 D、E、F 其中一个时则匹配成功,通过 server2-route 路由转发到 https://cn.bing.com/
总结
Spring Cloud Gateway 使用非常的灵活,可以根据不同的情况来进行路由分发,在实际项目中可以自由组合使用。同时 Spring Cloud Gateway 还有更多很酷的功能,比如 Filter、熔断和限流等。
【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

