企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # 1. gateway基本概念 * Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。 * Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的**路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。** * 内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。 * **过滤器分为Filter(内置20种) 和全局 Filter两种(内置9种)** ![](https://img.kancloud.cn/a9/48/a94860bf14437fb5331a84e5742b63a2_1502x494.png) ![](https://img.kancloud.cn/ac/e4/ace44633c2e8d75e73b403dffe2200ff_1794x1006.png) ![](https://img.kancloud.cn/66/85/6685de69532dfd4477b3b1dabeaccb8c_1862x716.png) # 2. gateway 请求路由 就是使用Predicate实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。 ## 2.1 通过时间匹配 Predicate 支持设置一个时间,在请求进行转发的时候,可以通过判断在这个时间之前或者之后进行转发。比如我们现在设置只有在 2019 年 1 月 1 日才会转发到我的网站,在这之前不进行转发,我就可以这样配置: ~~~ spring: cloud: gateway: routes: - id: time_route uri: http://ityouknow.com predicates: - After=2018-01-20T06:06:06+08:00[Asia/Shanghai] ~~~ Spring 是通过 ZonedDateTime 来对时间进行的对比,ZonedDateTime 是 Java 8 中日期时间功能里,用于表示带时区的日期与时间信息的类,ZonedDateTime 支持通过时区来设置时间,中国的时区是:`Asia/Shanghai`。 After Route Predicate 是指在这个时间之后的请求都转发到目标地址。上面的示例是指,请求时间在 2018 年 1 月 20 日 6 点 6 分 6 秒之后的所有请求都转发到地址`http://ityouknow.com`。`+08:00`是指时间和 UTC 时间相差八个小时,时间地区为`Asia/Shanghai`。 添加完路由规则之后,访问地址`http://localhost:8080`会自动转发到`http://ityouknow.com`。 Before Route Predicate 刚好相反,在某个时间之前的请求的请求都进行转发。我们把上面路由规则中的 After 改为 Before,如下: ~~~ spring: cloud: gateway: routes: - id: after_route uri: http://ityouknow.com predicates: - Before=2018-01-20T06:06:06+08:00[Asia/Shanghai] ~~~ 就表示在这个时间之前可以进行路由,在这时间之后停止路由,修改完之后重启项目再次访问地址`http://localhost:8080`,页面会报 404 没有找到地址。 除过在时间之前或者之后外,Gateway 还支持限制路由请求在某一个时间段范围内,可以使用 Between Route Predicate 来实现。 ~~~ spring: cloud: gateway: routes: - id: after_route uri: http://ityouknow.com predicates: - Between=2018-01-20T06:06:06+08:00[Asia/Shanghai], 2019-01-20T06:06:06+08:00[Asia/Shanghai] ~~~ 这样设置就意味着在这个时间段内可以匹配到此路由,超过这个时间段范围则不会进行匹配。通过时间匹配路由的功能很酷,可以用在限时抢购的一些场景中。 ## 2.2 通过 Cookie 匹配 Cookie Route Predicate 可以接收两个参数,一个是 Cookie name , 一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。 ~~~ spring: cloud: gateway: routes: - id: cookie_route uri: http://ityouknow.com predicates: - Cookie=ityouknow, kee.e ~~~ 使用 curl 测试,命令行输入: ~~~ curl http://localhost:8080 --cookie "ityouknow=kee.e" ~~~ 则会返回页面代码,如果去掉`--cookie "ityouknow=kee.e"`,后台汇报 404 错误。 ## 2.3 Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。 ~~~ spring: cloud: gateway: routes: - id: header_route uri: http://ityouknow.com predicates: - Header=X-Request-Id, \d+ ~~~ 使用 curl 测试,命令行输入: ~~~ curl http://localhost:8080 -H "X-Request-Id:666666" ~~~ 则返回页面代码证明匹配成功。将参数`-H "X-Request-Id:666666"`改为`-H "X-Request-Id:neo"`再次执行时返回 404 证明没有匹配。 ## 2.4 通过 Host 匹配 Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用`.`号作为分隔符。它通过参数中的主机地址作为匹配规则。 ~~~ spring: cloud: gateway: routes: - id: host_route uri: http://ityouknow.com predicates: - Host=**.ityouknow.com ~~~ 使用 curl 测试,命令行输入: ~~~ curl http://localhost:8080 -H "Host: www.ityouknow.com" curl http://localhost:8080 -H "Host: md.ityouknow.com" ~~~ 经测试以上两种 host 均可匹配到 host\_route 路由,去掉 host 参数则会报 404 错误。 ## 2.5 通过请求方式匹配 可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。 ~~~ spring: cloud: gateway: routes: - id: method_route uri: http://ityouknow.com predicates: - Method=GET ~~~ 使用 curl 测试,命令行输入: ~~~ # curl 默认是以 GET 的方式去请求 curl http://localhost:8080 ~~~ 测试返回页面代码,证明匹配到路由,我们再以 POST 的方式请求测试。 ~~~ # curl 默认是以 GET 的方式去请求 curl -X POST http://localhost:8080 ~~~ 返回 404 没有找到,证明没有匹配上路由 ## 2.6 通过请求路径匹配 Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。 ~~~ spring: cloud: gateway: routes: - id: host_route uri: http://ityouknow.com predicates: - Path=/foo/{segment} ~~~ 如果请求路径符合要求,则此路由将匹配,例如:/foo/1 或者 /foo/bar。 `- Path=/foo/** ` 配置所有/foot开头的请求 使用 curl 测试,命令行输入: ~~~ curl http://localhost:8080/foo/1 curl http://localhost:8080/foo/xx curl http://localhost:8080/boo/xx ~~~ 经过测试第一和第二条命令可以正常获取到页面返回值,最后一个命令报 404,证明路由是通过指定路由来匹配。 ## 2.7 通过请求参数匹配 Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。 ~~~ spring: cloud: gateway: routes: - id: query_route uri: http://ityouknow.com predicates: - Query=smile ~~~ 这样配置,只要请求中包含 smile 属性的参数即可匹配路由。 使用 curl 测试,命令行输入: ~~~ curl localhost:8080?smile=x&id=2 ~~~ 经过测试发现只要请求汇总带有 smile 参数即会匹配路由,不带 smile 参数则不会匹配。 还可以将 Query 的值以键值对的方式进行配置,这样在请求过来时会对属性值和正则进行匹配,匹配上才会走路由。 ~~~ spring: cloud: gateway: routes: - id: query_route uri: http://ityouknow.com predicates: - Query=keep, pu. ~~~ 这样只要当请求中包含 keep 属性并且参数值是以 pu 开头的长度为三位的字符串才会进行匹配和路由。 使用 curl 测试,命令行输入: ~~~ curl localhost:8080?keep=pub ~~~ 测试可以返回页面代码,将 keep 的属性值改为 pubx 再次访问就会报 404, 证明路由需要匹配正则表达式才会进行路由。 ## 2.8 通过请求 ip 地址进行匹配 Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号 (IPv4 或 IPv6) 字符串的列表(最小大小为 1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子网掩码)。 ~~~ spring: cloud: gateway: routes: - id: remoteaddr_route uri: http://ityouknow.com predicates: - RemoteAddr=192.168.1.1/24 ~~~ 可以将此地址设置为本机的 ip 地址进行测试。 果请求的远程地址是 192.168.1.10,则此路由将匹配。 ## 2.9 组合使用 上面为了演示各个 Predicate 的使用,我们是单个单个进行配置测试,其实可以将各种 Predicate 组合起来一起使用。 例如: ~~~ spring: cloud: gateway: routes: - id: host_foo_path_headers_to_httpbin uri: http://ityouknow.com predicates: - Host=**.foo.org - Path=/headers - Method=GET - Header=X-Request-Id, \d+ - Query=foo, ba. - Query=baz - Cookie=chocolate, ch.p - After=2018-01-20T06:06:06+08:00[Asia/Shanghai] ~~~ ## 方式二:编码 以上是基于配置文件方式,还可以编码的方式添加路由 配置访问路由端口`/guonei`链接到`http://news.baidu.com/guonei`的示例代码 ~~~ 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; @Configuration public class GatewayConfigration { @Bean public RouteLocator myRouteLocator(RouteLocatorBuilder builder){ RouteLocatorBuilder.Builder routes=builder.routes(); routes.route("my_path_routh",r->r.path("/guonei").uri("http://news.baidu.com/guonei")); return routes.build(); } } ~~~ # 3. gateway请求过滤 filter * predicate断言是对请求路由处理,找到对应的后台服务,而在路由处理之前,需要经过`pre`类型的filter,处理放回响应后由`post`类型的请求处理,和mvc的过滤器类似。 * 在`pre`类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等 * 在`post`类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等。 ## 3.1 生命周期 Spring Cloud Gateway同zuul类似,有“pre”和“post”两种方式的filter。客户端的请求先经过“pre”类型的filter,然后将请求转发到具体的业务服务,比如上图中的user-service,收到业务服务的响应之后,再经过“post”类型的filter处理,最后返回响应到客户端。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190601011115924.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9mb3JlenAuYmxvZy5jc2RuLm5ldA==,size_16,color_FFFFFF,t_70) 与zuul不同的是,filter除了分为“pre”和“post”两种方式的filter外,在Spring Cloud Gateway中,filter从作用范围可分为另外两种,一种是针对于单个路由的gateway filter,它在配置文件中的写法同predict类似;另外一种是针对于所有路由的global gateway filer。现在从作用范围划分的维度来讲解这两种filter。 ## 3.2 AddRequestHeader GatewayFilter Factory **增加请求头过滤器** 创建工程,引入相关的依赖,包括spring boot 版本2.0.5,spring Cloud版本Finchley,gateway依赖如下: ~~~ <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> ~~~ 在工程的配置文件中,加入以下的配置: ~~~text server: port: 8081 spring: profiles: active: add_request_header_route --- spring: cloud: gateway: routes: - id: add_request_header_route uri: http://httpbin.org:80/get filters: - AddRequestHeader=X-Request-Foo, Bar predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] profiles: add_request_header_route ~~~ 在上述的配置中,工程的启动端口为8081,配置文件为add\_request\_header\_route,在add\_request\_header\_route配置中,配置了roter的id为add\_request\_header\_route,路由地址为http://httpbin.org:80/get,该router有AfterPredictFactory,有一个filter为AddRequestHeaderGatewayFilterFactory(约定写成AddRequestHeader),AddRequestHeader过滤器工厂会在请求头加上一对请求头,名称为X-Request-Foo,值为Bar。为了验证AddRequestHeaderGatewayFilterFactory是怎么样工作的,查看它的源码,AddRequestHeaderGatewayFilterFactory的源码如下: ~~~ public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply(NameValueConfig config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest().mutate() .header(config.getName(), config.getValue()) .build(); return chain.filter(exchange.mutate().request(request).build()); }; } } ~~~ 由上面的代码可知,根据旧的ServerHttpRequest创建新的 ServerHttpRequest ,在新的ServerHttpRequest加了一个请求头,然后创建新的 ServerWebExchange ,提交过滤器链继续过滤。 启动工程,通过curl命令来模拟请求: ~~~ curl localhost:8081 ~~~ 最终显示了从 http://httpbin.org:80/get得到了请求,响应如下: ~~~ { "args": {}, "headers": { "Accept": "*/*", "Connection": "close", "Forwarded": "proto=http;host=\"localhost:8081\";for=\"0:0:0:0:0:0:0:1:56248\"", "Host": "httpbin.org", "User-Agent": "curl/7.58.0", "X-Forwarded-Host": "localhost:8081", "X-Request-Foo": "Bar" }, "origin": "0:0:0:0:0:0:0:1, 210.22.21.66", "url": "http://localhost:8081/get" } ~~~ 可以上面的响应可知,确实在请求头中加入了X-Request-Foo这样的一个请求头,在配置文件中配置的AddRequestHeader过滤器工厂生效。 跟AddRequestHeader过滤器工厂类似的还有AddResponseHeader过滤器工厂,在此就不再重复。 ## 3.3 RewritePath GatewayFilter Factory **路径重写** 在Nginx服务启中有一个非常强大的功能就是重写路径,Spring Cloud Gateway默认也提供了这样的功能,这个功能是Zuul没有的。在配置文件中加上以下的配置: ~~~ spring: profiles: active: rewritepath_route --- spring: cloud: gateway: routes: - id: rewritepath_route uri: https://blog.csdn.net predicates: - Path=/foo/** filters: - RewritePath=/foo/(?<segment>.*), /$\{segment} profiles: rewritepath_route ~~~ 上面的配置中,所有的/foo/\*\*开始的路径都会命中配置的router,并执行过滤器的逻辑,在本案例中配置了RewritePath过滤器工厂,此工厂将/foo/(?.\*)重写为{segment},然后转发到https://blog.csdn.net。比如在网页上请求localhost:8081/foo/forezp,此时会将请求转发到https://blog.csdn.net/forezp的页面,比如在网页上请求localhost:8081/foo/forezp/1,页面显示404,就是因为不存在https://blog.csdn.net/forezp/1这个页面。 ## 3.4 自定义过滤器 Spring Cloud Gateway内置了19种强大的过滤器工厂,能够满足很多场景的需求,那么能不能自定义自己的过滤器呢,当然是可以的。在spring Cloud Gateway中,过滤器需要实现GatewayFilter和Ordered2个接口。写一个RequestTimeFilter,代码如下: 统计请求耗时: ### 3.4.1 定义filter ~~~ public class RequestTimeFilter implements GatewayFilter, Ordered { private static final Log log = LogFactory.getLog(GatewayFilter.class); private static final String REQUEST_TIME_BEGIN = "requestTimeBegin"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis()); return chain.filter(exchange).then( Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN); if (startTime != null) { log.info(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms"); } }) ); } @Override public int getOrder() { return 0; } } ~~~ 在上面的代码中,Ordered中的int getOrder()方法是来给过滤器设定优先级别的,值越大则优先级越低。还有有一个filterI(exchange,chain)方法,在该方法中,先记录了请求的开始时间,并保存在ServerWebExchange中,此处是一个“pre”类型的过滤器,然后再chain.filter的内部类中的run()方法中相当于"post"过滤器,在此处打印了请求所消耗的时间。 ### 3.4.2 注册filter 然后将该过滤器注册到router中,代码如下: ~~~ @Bean public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route(r -> r.path("/customer/**") .filters(f -> f.filter(new RequestTimeFilter()) .addResponseHeader("X-Response-Default-Foo", "Default-Bar")) .uri("http://httpbin.org:80/get") .order(0) .id("customer_filter_router") ) .build(); } ~~~ 重启程序,通过curl命令模拟请求: ~~~ curl localhost:8081/customer/123 ~~~ 在程序的控制台输出一下的请求信息的日志: ~~~ 2018-11-16 15:02:20.177 INFO 20488 --- [ctor-http-nio-3] o.s.cloud.gateway.filter.GatewayFilter : /customer/123: 152ms ~~~ ## 3.5 自定义过滤器工厂 在上面的自定义过滤器中,有没有办法自定义过滤器工厂类呢?这样就可以在配置文件中配置过滤器了。现在需要实现一个过滤器工厂,在打印时间的时候,可以设置参数来决定是否打印请参数。查看GatewayFilterFactory的源码,可以发现GatewayFilterfactory的层级如下: ![微信截图_20181204175448.png](https://imgconvert.csdnimg.cn/aHR0cHM6Ly93d3cuZmFuZ3poaXBlbmcuY29tL2ltZy9qaWFuc2h1LzIyNzk1OTQtZjk3ZTEwNDVjZWJjOTU0Yy5wbmc?x-oss-process=image/format,png) 过滤器工厂的顶级接口是GatewayFilterFactory,我们可以直接继承它的两个抽象类来简化开发AbstractGatewayFilterFactory和AbstractNameValueGatewayFilterFactory,这两个抽象类的区别就是前者接收一个参数(像StripPrefix和我们创建的这种),后者接收两个参数(像AddResponseHeader)。 过滤器工厂的顶级接口是GatewayFilterFactory,有2个两个较接近具体实现的抽象类,分别为AbstractGatewayFilterFactory和AbstractNameValueGatewayFilterFactory,这2个类前者接收一个参数,比如它的实现类RedirectToGatewayFilterFactory;后者接收2个参数,比如它的实现类AddRequestHeaderGatewayFilterFactory类。现在需要将请求的日志打印出来,需要使用一个参数,这时可以参照RedirectToGatewayFilterFactory的写法。 ~~~ public class RequestTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestTimeGatewayFilterFactory.Config> { private static final Log log = LogFactory.getLog(GatewayFilter.class); private static final String REQUEST_TIME_BEGIN = "requestTimeBegin"; private static final String KEY = "withParams"; @Override public List<String> shortcutFieldOrder() { return Arrays.asList(KEY); } public RequestTimeGatewayFilterFactory() { super(Config.class); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis()); return chain.filter(exchange).then( Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN); if (startTime != null) { StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath()) .append(": ") .append(System.currentTimeMillis() - startTime) .append("ms"); if (config.isWithParams()) { sb.append(" params:").append(exchange.getRequest().getQueryParams()); } log.info(sb.toString()); } }) ); }; } public static class Config { private boolean withParams; public boolean isWithParams() { return withParams; } public void setWithParams(boolean withParams) { this.withParams = withParams; } } } ~~~ 在上面的代码中 apply(Config config)方法内创建了一个GatewayFilter的匿名类,具体的实现逻辑跟之前一样,只不过加了是否打印请求参数的逻辑,而这个逻辑的开关是config.isWithParams()。静态内部类类Config就是为了接收那个boolean类型的参数服务的,里边的变量名可以随意写,但是要重写List shortcutFieldOrder()这个方法。 。 需要注意的是,在类的构造器中一定要调用下父类的构造器把Config类型传过去,否则会报ClassCastException 最后,需要在工程的启动文件Application类中,向Srping Ioc容器注册RequestTimeGatewayFilterFactory类的Bean。 ~~~ @Bean public RequestTimeGatewayFilterFactory elapsedGatewayFilterFactory() { return new RequestTimeGatewayFilterFactory(); } ~~~ 然后可以在配置文件中配置如下: ~~~ spring: profiles: active: elapse_route --- spring: cloud: gateway: routes: - id: elapse_route uri: http://httpbin.org:80/get filters: - RequestTime=false predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] profiles: elapse_route ~~~ 启动工程,在浏览器上访问localhost:8081?name=forezp,可以在控制台上看到,日志输出了请求消耗的时间和请求参数。 ## 3.6 global filter Spring Cloud Gateway根据作用范围划分为GatewayFilter和GlobalFilter,二者区别如下: * GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上 * GlobalFilter : **全局过滤器,不需要在配置文件中配置,作用在所有的路由上**,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。 Spring Cloud Gateway框架内置的GlobalFilter如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190601011412503.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9mb3JlenAuYmxvZy5jc2RuLm5ldA==,size_16,color_FFFFFF,t_70) 上图中每一个GlobalFilter都作用在每一个router上,能够满足大多数的需求。但是如果遇到业务上的定制,可能需要编写满足自己需求的GlobalFilter。在下面的案例中将讲述如何编写自己GlobalFilter,该GlobalFilter会校验请求中是否包含了请求参数“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。代码如下: ~~~ public class TokenFilter implements GlobalFilter, Ordered { Logger logger=LoggerFactory.getLogger( TokenFilter.class ); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst("token"); if (token == null || token.isEmpty()) { logger.info( "token is empty..." ); exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return -100; } } ~~~ 在上面的TokenFilter需要实现GlobalFilter和Ordered接口,这和实现GatewayFilter很类似。然后根据ServerWebExchange获取ServerHttpRequest,然后根据ServerHttpRequest中是否含有参数token,如果没有则完成请求,终止转发,否则执行正常的逻辑。 然后需要将TokenFilter在工程的启动类中注入到Spring Ioc容器中,代码如下: ~~~java @Bean public TokenFilter tokenFilter(){ return new TokenFilter(); } ~~~ 启动工程,使用curl命令请求: ~~~ curl localhost:8081/customer/123 ~~~ 可以看到请没有被转发,请求被终止,并在控制台打印了如下日志: ~~~ 2018-11-16 15:30:13.543 INFO 19372 --- [ctor-http-nio-2] gateway.TokenFilter : token is empty... ~~~ 上面的日志显示了请求进入了没有传“token”的逻辑。 ## 总结 本篇文章讲述了Spring Cloud Gateway中的过滤器,包括GatewayFilter和GlobalFilter。从官方文档的内置过滤器讲起,然后讲解自定义GatewayFilter、GatewayFilterFactory以及自定义的GlobalFilter。有很多内置的过滤器并没有讲述到,比如限流过滤器,这个我觉得是比较重要和大家关注的过滤器,将在之后的文章讲述。