目录
- 概述
- 工作流程
- 入门配置
- 路由映射
- GateWay高级特性
- Route 以服务名动态获取服务uri
- Predicate断言(谓词)
- The After Route Predicate Factory
- The Before Route Predicate Factory
- The Between Route Predicate Factory
- The Cookie Route Predicate Factory
- The Header Route Predicate Factory
- The Host Route Predicate Factory
- The Method Route Predicate Factory
- The Path Route Predicate Factory
- The Query Route Predicate Factory
- The RemoteAddr Route Predicate Factory
- The Weight Route Predicate Factory
- The XForwarded Remote Addr Route Predicate Factory
- 自定义断言
- Filter 过滤
- 相关文献
概述
Gateway是在Spring生态系统之上构建的API网关服务,基于Spring6,Spring Boot 3和Project Reactor等技术。它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式,并为它们提供跨领域的关注点,例如:安全性、监控/度量和恢复能力。
在Spring Cloud中,在1.x版本中都是采用的Zuul网关;
但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul
微服务架构网关位置图如下:
作用
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
总结:
Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。
Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务结点IP端口信息,从而加强安全保护。
Spring Cloud Gateway本身也是一个微服务,需要注册进服务注册中心(像consul)。
GateWay三大核心
- Route-路由
网关的基本构建块。它由 ID、目标 URI、谓词集合和筛选器集合定义。如果聚合谓词为 true,则匹配路由。 - Predicate-断言
这是一个 Java 8 函数谓词。输入类型是 Spring Framework ServerWebExchange 这使您可以匹配 HTTP 请求中的任何内容,例如标头或参数,如果请求和断言相匹配则进行路由。 - Filter-过滤器
这些是使用特定工厂构建的 GatewayFilter 实例。在这里,您可以在发送下游请求之前或之后修改请求和响应。
application.yml配置示例
GateWay有两种配置展现形式,Shortcut Configuration(快捷方式配置)、Fully Expanded Arguments(完全展开的参数)
Shortcut Configuration:
spring:
cloud:
gateway:
routes:
- id: after_route #我们自定义的路由 ID,保持唯一
uri: https://example.org #目标服务地址
predicates: #路由条件,Predicate接受一个输入参数返回一个布尔值。
- Cookie=mycookie,mycookievalue #该属性包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)
Fully Expanded Arguments:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue
图理解
如下图
web前端请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
predicate就是我们的匹配条件;
filter,就可以理解为一个无所不能的过滤器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
举个例子:
我要去华为专卖店买手机,那么我肯定不能去苹果专卖店去,那么这个去哪个店就是路由,我到了华为专卖店,我要买华为黑色的mate60pro,店里有黑色、白色的,刚好有我想要的黑色,那么颜色匹配上我想要的,这个判断就是断言,如果没有匹配上,那我就买不了,我买上了心爱的手机,这时候专卖店给我送了个屏幕膜,这时候给我贴上了给我,举动非常贴心,在我买手机后给我在裸机上贴了个膜,这就是过滤器的post动作。
工作流程
客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(Pre)或之后(Post)执行业务逻辑。
在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;
在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
核心逻辑就是:路由转发+断言判断+执行过滤器链
入门配置
网关依赖引入:
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
将网关服务注册进去(只提供思路,具体代码实现可参考其他文章 ,关键词:consul服务注册)
路由映射
诉求:不想暴露真实的ip地址、服务名等等给外部使用,在外面套一层网关
新起一个服务要求访问:http://localhost:8001/pay/gateway/get/1,http://localhost:8001/pay/gateway/info,可通
网关服务yml新增配置:
然后重启网关服务。
如网关yml配置正确,则访问http://localhost:9527/pay/gateway/get/1,http://localhost:9527/pay/gateway/info可通,这样就达到了,外部服务只需要知道9527端口,不知实际8001真实端口的要求。
GateWay高级特性
Route 以服务名动态获取服务uri
上面的例子很简单,但是问题也来了,一个服务简单的写写ip端口没问题,如果是部署集群呢,会很麻
怎么解决呢?
网关yml修改前:
网关yml修改后:通过lb后面跟着服务名,这样就能不用关心服务的ip端口了
Predicate断言(谓词)
Spring Cloud Gateway 将路由作为 Spring WebFlux HandlerMapping 基础结构的一部分进行匹配。Spring Cloud Gateway 包含许多内置的路由谓词工厂。所有这些谓词都与 HTTP 请求的不同属性匹配。您可以将多个路由谓词工厂与逻辑 and 语句组合在一起。以下图右侧是内置的断言。
The After Route Predicate Factory
After 路由谓词工厂采用一个参数 a datetime (即 java ZonedDateTime )。此谓词匹配在指定日期时间之后发生的请求。以下示例配置路由后谓词:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
案例说明:只能通过2017 年 1 月 20 日 17:42 Mountain Time (Denver) 之后的请求
After、Before、Between 时间参数获取方式:
ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
The Before Route Predicate Factory
Before 路由谓词工厂采用一个参数 a datetime (即 java ZonedDateTime )。此谓词匹配在指定的 datetime .以下示例配置 before route 谓词:
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
案例说明:只能通过 2017 年 1 月 20 日 17:42 Mountain Time (Denver) 之前的请求
The Between Route Predicate Factory
Between 路由谓词工厂采用两个参数, datetime1 datetime2 它们是 java ZonedDateTime 对象。此谓词匹配在 datetime1 之后和之前 datetime2 发生的请求。该 datetime2 参数必须位于 datetime1 之后。以下示例配置 between route 谓词:
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
案例说明:只能通过2017 年 1 月 20 日 17:42 山地时间(丹佛)之后和 2017 年 1 月 21 日 17:42 山地时间(丹佛)之前的请求
时间的之前、之后、中间,很符合现在活动业务中的秒杀业务。
The Cookie Route Predicate Factory
Cookie 路由谓词工厂采用两个参数,cookie name 和 a regexp (Java 正则表达式)。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。以下示例配置 cookie 路由谓词工厂:
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
案例说明:
此路由匹配具有名为 chocolate cookie 的请求,其值与 ch.p 正则表达式匹配。
The Header Route Predicate Factory
Header 路由谓词工厂采用两个参数,即 和 header a regexp (这是一个 Java 正则表达式)。此谓词与具有给定名称的标头匹配,其值与正则表达式匹配。以下示例配置标头路由谓词:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
案例说明:
如果请求具有名为 X-Request-Id 其值与 \d+ 正则表达式匹配的标头(即,它的值为一位或多位),则此路由匹配。
The Host Route Predicate Factory
Host 路由谓词工厂采用一个参数:主机名 patterns 列表。该图案是蚂 . 蚁风格的图案,作为分隔符。此谓词与 Host 与模式匹配的标头匹配。以下示例配置主机路由谓词:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
案例说明:
还支持 URI 模板变量(如 {sub}.myhost.org )。如果请求的 Host 标头值为 www.somehost.org or beta.somehost.org 或 www.anotherhost.org ,则此路由匹配。
The Method Route Predicate Factory
Method 路由谓词工厂采用一个 methods 参数,该参数是一个或多个参数:要匹配的 HTTP 方法。下面的示例配置方法路由谓词:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
案例说明:
如果请求方法是 a GET 或 . POST则匹配
The Path Route Predicate Factory
Path 路由谓词工厂采用两个参数:Spring PathMatcher patterns 列表和名为 matchTrailingSlash (默认为 true ) 的可选标志。以下示例配置路径路由谓词:
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
案例说明:
如果请求路径为,则此路由匹配,例如: /red/1 或 /red/1/ 或 /red/blue 或/blue/green 。
The Query Route Predicate Factory
Query 路由谓词工厂采用两个参数:必需 param 参数和可选参数 regexp (Java 正则表达式)。以下示例配置查询路由谓词:
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
案例说明:
如果请求包含 green 查询参数,则上述路由匹配。
The RemoteAddr Route Predicate Factory
RemoteAddr 路由谓词工厂采用 sources 的列表(最小大小为 1),这些列表是 CIDR 表示法(IPv4 或 IPv6)字符串,例如 192.168.0.1/16 (where 192.168.0.1 是 IP 地址, 16 是子网掩码)。以下示例配置 RemoteAddr 路由谓词:
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
案例说明:
如果请求的远程地址为 ,则此路由匹配 192.168.1.10
The Weight Route Predicate Factory
Weight 路由谓词工厂采用两个参数: group 和 weight (一个 int)。权重是按组计算的。以下示例配置权重路由谓词:
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
案例说明:
此路由会将 ~80% 的流量转发到 weighthigh.org,将 ~20% 的流量转发到 weightlow.org
The XForwarded Remote Addr Route Predicate Factory
XForwarded Remote Addr 路由谓词工厂采用 sources 的列表(最小大小为 1),这些列表是 CIDR 表示法(IPv4 或 IPv6)字符串,例如 192.168.0.1/16 (where 192.168.0.1 是 IP 地址, 16 是子网掩码)。此路由谓词允许根据 X-Forwarded-For HTTP 标头筛选请求。这可用于反向代理,例如负载平衡器或 Web 应用程序防火墙,其中仅当请求来自这些反向代理使用的受信任 IP 地址列表时,才应允许请求。
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- XForwardedRemoteAddr=192.168.1.1/24
案例说明:
如果 X-Forwarded-For 标头包含,例如, 192.168.1.10 此路由匹配。
自定义断言
如果以上内置断言不满足需求,那么可以自定义断言,可参考AbstractRoutePredicateFactory类源码,实现自定义断言
步骤:
1、新建XXXRoutePredicateFactory,要以RoutePredicateFactory结尾,且继承AbstractRoutePredicateFactory类
2、重写apply方法
3、新建config内部类,用来断言的重要key
4、空参调用方法,内部调用sup
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config>
{
public MyRoutePredicateFactory()
{
super(MyRoutePredicateFactory.Config.class);
}
@Validated
public static class Config{
@Setter
@Getter
@NotEmpty
private String userType; //钻、金、银等用户等级
}
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config)
{
return new Predicate<ServerWebExchange>()
{
@Override
public boolean test(ServerWebExchange serverWebExchange)
{
//检查request的参数里面,userType是否为指定的值,符合配置就通过
String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
if (userType == null) return false;
//如果说参数存在,就和config的数据进行比较
if(userType.equals(config.getUserType())) {
return true;
}
return false;
}
};
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("userType");
}
}
yml配置(自定义断言要用Fully Expanded Arguments(完全展开的参数),如果用Shortcut Configuration会报错!):
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- name: My
args:
userType: diamond
Filter 过滤
路由过滤器允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。路由筛选器的作用域为特定路由。Spring Cloud Gateway 包含许多内置的 GatewayFilter 工厂。
GateWay过滤器分Global Filters(全局默认过滤器)和GateWay Filter(单一内置过滤器),区别在于,全局默认过滤器是作用于所有路由,不需要在yml中配置,实现GlobalFilter接口即可,单一内置过滤器需要在yml中配置,作用于单一路由或者路由组。
单一内置过滤器
官网有几十种,在这里我就不一一举例了,分了几组给大家看看只举几个例子
请求头相关
AddRequestHeader GatewayFilter Factory
采用 and name value 参数。以下示例配置: AddRequestHeader GatewayFilter
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-red, blue
此列表将标头添加到 X-Request-red:blue 所有匹配请求的下游请求的标头中。
RemoveRequestHeader GatewayFilter Factory
RemoveRequestHeader GatewayFilter 工厂采用一个 name 参数。它是要删除的标头的名称。以下列表配置:
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
这将在标 X-Request-Foo 头发送到下游之前将其删除。
SetRequestHeader GatewayFilter Factory
SetRequestHeader GatewayFilter 工厂采取 name 和 value 参数。以下列表配置: SetRequestHeader GatewayFilter
spring:
cloud:
gateway:
routes:
- id: setrequestheader_route
uri: https://example.org
filters:
- SetRequestHeader=X-Request-Red, Blue
这会 GatewayFilter 用给定的名称替换(而不是添加)所有标头。因此,如果下游服务器响应 X-Request-Red:1234 ,它将被替换为 X-Request-Red:Blue ,这是下游服务将接收的内容。
请求参数相关
AddRequestParameter GatewayFilter Factory
AddRequestParameter GatewayFilter 工厂采用 and name value 参数。以下示例配置:
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=red, blue
这将添加到 red=blue 所有匹配请求的下游请求的查询字符串中。
RemoveRequestParameter GatewayFilter Factory
RemoveRequestParameter GatewayFilter 工厂采用一个 name 参数。它是要删除的查询参数的名称。以下示例配置:
spring:
cloud:
gateway:
routes:
- id: removerequestparameter_route
uri: https://example.org
filters:
- RemoveRequestParameter=red
这将在 red 参数发送到下游之前将其删除。
响应头相关
AddResponseHeader GatewayFilter Factory
AddResponseHeader GatewayFilter 工厂采用 and name value 参数。以下示例配置:
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Red, Blue
这会将标头添加到 X-Response-Red:Blue 所有匹配请求的下游响应标头中。
SetResponseHeader GatewayFilter Factory
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: https://example.org
filters:
- SetResponseHeader=X-Response-Red, Blue
此 GatewayFilter 将所有标头替换(而不是添加)为给定名称。因此,如果下游服务器响应 X-Response-Red:1234 ,它将被替换为 X-Response-Red:Blue ,这是网关客户端将接收的内容。
RemoveResponseHeader GatewayFilter Factory
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: https://example.org
filters:
- RemoveResponseHeader=X-Response-Foo
这将在将 X-Response-Foo 标头返回到网关客户端之前从响应中删除标头。
前缀路径相关
PrefixPath GatewayFilter Factory
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
这为所有匹配请求的路径添加前缀 /mypath 。因此,将请求 /hello 发送到 /mypath/hello
SetPath GatewayFilter Factory
SetPath GatewayFilter 工厂采用路径 template 参数。它提供了一种简单的方法来操作请求路径,允许路径的模板化段。这使用 Spring Framework 中的 URI 模板。允许多个匹配的区段。以下示例配置:
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- SetPath=/{segment}
对于请求 /red/blue 路径 ,这会在发出下游请求之前设置路径
{segment}就是个占位符,等价于SetPath后面指定的{segment}内容
RedirectTo GatewayFilter Factory
RedirectTo GatewayFilter 工厂采用三个参数, status 、 url 和 includeRequestParams 可选 。该 status 参数应为 300 系列重定向 HTTP 代码,例如 301。该 url 参数应为有效的 URL。这是 Location 标头的值。该 includeRequestParams 参数指示是否应将请求查询参数包含在 url 上。如果未设置,它将被视为 false .对于相对重定向,应用作 uri: no://op 路由定义的 uri。以下列表配置:
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- RedirectTo=302, https://acme.org
这将发送带有 Location:https://acme.org 标头的状态 302 以执行重定向。
其他
Default Filters
要添加过滤器并将其应用于所有路由,可以使用 spring.cloud.gateway.default-filters .此属性采用筛选器列表。以下列表定义了一组默认筛选器:
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin
这个就功能类似于全局默认过滤器,作用于所有路由的过滤
自定义单一内置过滤器
查看SetPathGatewayFilterFactory源码,参考源码实现
新建MyGatewayFilterFactory类,以XXXXGatewayFilterFactory文件名结尾
创建xxGatewayFilterFactory.Config内部类
重写apply、shortcutFieldOrder方法
空参构造方法,重写super
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config>
{
public MyGatewayFilterFactory()
{
super(MyGatewayFilterFactory.Config.class);
}
@Override
public GatewayFilter apply(MyGatewayFilterFactory.Config config)
{
return new GatewayFilter()
{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
ServerHttpRequest request = exchange.getRequest();
System.out.println("进入了自定义网关过滤器MyGatewayFilterFactory,status:"+config.getStatus());
if(request.getQueryParams().containsKey("atguigu")){
return chain.filter(exchange);
}else{
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
}
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("status");
}
public static class Config
{
@Getter@Setter
private String status;//设定一个状态值/标志位,它等于多少,匹配和才可以访问
}
}
//单一内置过滤器GatewayFilter
yml:
spring:
cloud:
gateway:
routes:
- id: gateway_1
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/filter/**
filters:
- My=xixi
访问http://localhost:9527/pay/gateway/filter?xixi=dabai 通
全局默认过滤器
当请求与路由匹配时,筛选 Web 处理程序会 GatewayFilter 将 的所有 GlobalFilter 实例和所有特定于路由的实例添加到筛选器链中。
由于Spring Cloud Gateway区分了过滤器逻辑执行的“pre”和“post”阶段(请参阅工作原理),因此优先级最高的过滤器是“pre”阶段的第一个,最后一个是“post”阶段的最后一个。
官网代码
自定义全局过滤器实现统计服务接口请求耗时
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered
{
/**
* 数字越小优先级越高
* @return
*/
@Override
public int getOrder()
{
return 0;
}
private static final String BEGIN_VISIT_TIME = "begin_visit_time";//开始访问时间
/**
*第2版,各种统计
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//先记录下访问接口的开始时间
exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(()->{
Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
if (beginVisitTime != null){
log.info("访问接口主机: " + exchange.getRequest().getURI().getHost());
log.info("访问接口端口: " + exchange.getRequest().getURI().getPort());
log.info("访问接口URL: " + exchange.getRequest().getURI().getPath());
log.info("访问接口URL参数: " + exchange.getRequest().getURI().getRawQuery());
log.info("访问接口时长: " + (System.currentTimeMillis() - beginVisitTime) + "ms");
log.info("我是美丽分割线: ###################################################");
System.out.println();
}
}));
}
}
效果图
相关文献
官网:https://docs.spring.io/spring-cloud-gateway/reference/index.html
就先说到这
\color{#008B8B}{ 就先说到这}
就先说到这
在下
A
p
o
l
l
o
\color{#008B8B}{在下Apollo}
在下Apollo
一个爱分享
J
a
v
a
、生活的小人物,
\color{#008B8B}{一个爱分享Java、生活的小人物,}
一个爱分享Java、生活的小人物,
咱们来日方长,有缘江湖再见,告辞!
\color{#008B8B}{咱们来日方长,有缘江湖再见,告辞!}
咱们来日方长,有缘江湖再见,告辞!