文章目录
- 1 gateway 知识图谱
- 2 什么是spring cloud gateway
- 3 什么是服务网关
- 4 为什么要使用网关
- 5 网关解决了什么问题
- 6 常用网关解决方案
- 6.1 nginx+lua
- 6.2 kong
- 6.3 Traefik
- 6.4 spring cloud netflix zuul
- 6.5 spring cloud Gateway
- 7 nginx实现网关
- 7.1 路由工作原理
- 8 路由规则
- 8.1 after
- 8.2 Before
- 8.3 Between
- 8.4 [Cookie 路由谓词工厂](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-cookie-route-predicate-factory)
- 8.5 header
- 8.6 HOST
- 8.7 Method
- 8.8 Path(常用)
- 8.9 Query
- 8.10 RemoteAddr
- 8.11 Weight
- 8.12 java正则表达式
- 9 动态路由(服务于发现路由规则)
- 10 过滤器
- 10.1 过滤器
- 10.2 全局过滤器
- 10.3 自定义过滤器
- 10.3.1 自定义网关过滤器
- 10.3.2 自定义全局过滤器
- 10.3.3 统一鉴权
- 11 cors配置
- 12 配置访问日志
- 13 开发指南
- 13.1 [编写自定义路由谓词工厂](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#writing-custom-route-predicate-factories)
- 13.2 [编写自定义网关过滤器工厂](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#writing-custom-gatewayfilter-factories)
- 13.3 自定义网关过滤器的命名规范
- 13.4 编写自定义全局过滤器
1 gateway 知识图谱
2 什么是spring cloud gateway
spring cloud gateway 是springcloud 生态系统中的网关,目标是代替zuul,其不仅提供统一的路由方式,并且还基于filter链的方式提供网关基本的功能.目前最新的springcloud中引用的还是zuul 1.x版本,而这个版本是基于过滤器的,是阻塞IO,不支持长连接
zuul 2.x 版本一直跳票,2019年5月,netflix终于开源支持异步调用模式的zuul2.0版本。可惜的是spring cloud 已经不在集成zuul2.x。这是我们学习 spring cloud gateway的前置原因。
spring cloud gateway 是基于spring生态系统之上构建的API 网关,包括:spring5、springboot2 和project Reactor。spring cloud Gateway 提供一种简单有效的方法来路由到api。并为他们提供跨域的关注点。例如:安全性,监视\指标、限流等。由于spring5.0 支持netty HTTP2 ,而springboot 2.0支持spring5.0,因此spring cloud gateway支持netty和http2顺理成章。
3 什么是服务网关
api gateway,是出现在系统边界上的一个面向api的、串行集中式强管控服务,这里的边界是企业IT系统的边界,可以理解为企业及应用防火墙。主要起到隔离外部方位与内部系统的作用。在微服务概念流行之前,api网关就已经诞生了,例如:银行、证券等灵越常见的前置机系统,可以也是解决访问认证、报文转换,访问统计等问题。
随着微服务架构概念的提出,api 网关成了微服务架构的一个标配组件。
api网关是一个服务器,是系统对外唯一入口。api网关封装了系统内部架构,为每个客户提定制的api。所有与的客户端和消费端都通过统一的网关介入微服务,在网关层处理所有非业务功能。api网关并不是微服务场景必须的组件。
对于服务数量众多,复杂度比较高,规模比较大的业务来说,引入api网关也有一系统的好处。
- 聚合接口使得服务队调用者透明,客户端和后端的耦合度降低
- 聚合后台服务,节省流量,提高性能,提升用户体验
- 提供安全、流控、过滤、缓存、计费、监控等api管理功能
4 为什么要使用网关
- 单体应用:浏览器发起请求到单体应用的服务器所在的机器上,应用从数据库查询数据原路返回给浏览器。对于单体应用来说是不需要网关的。
- 微服务:微服务的应用可能部署在不同的机房、不同地域、不同的域名下。此时客户端想要请求对应服务,都需要知道机器真实的ip或者域名url,当微服务实例众多时,这个非常难以记忆的,对于客户端来说太复杂难以维护。此时就有了网关。客户端相关请求直接发动到网关,由网关更加请求标识解析判断出具体的微服务地址,在吧请求转发到微服务实例。这个记忆功能就全部交给了网关来操作。
总结:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性
- 存在跨域请求,在一定场景下处理相对复杂
- 身份认证问题,每个微服务需要独立身份认证
- 难以重构,随着项目迭代可能需要重新话费微服务
- 某些微服务可能使用了防火墙,浏览器不友好协议,直接访问会有一定的困难
因此,我们需要网关结余客户端和服务器之间的中间层,所有的外部请求睢县进过微服务网关,客户端只要需要与网关交互,只需要知道网关地址即可:这样便于开发且有一下有点:
- 易于监控,在微服务网关收集监控数据并将其推送到外部系统进行分析
- 易于认证,在微服务网关进行认证,然后再将请求转发到后端的微服务,从而无需再每个为服务器中进行认证。
- 减少了客户端与各个微服务之间的交互次数
5 网关解决了什么问题
网关具有身份认证与安全,审查与监控、动态路由、负载均衡、缓存、请求分片与管理、静态响应处理等功能。当然最主要的职责还是与外界联系
- 性能:api 高可能,负载均衡,容错机制。
- 安全:权限身份认证、脱敏、流量清洗,后端签名(保证链路可信调用)。黑名单(非法调用的限制)
- 日志:日志记录,一旦涉及分布式,全链路更加比不可少
- 缓存:数据缓存
- 监控:记录请求响应数据,api耗时分析,性能监控
- 限流:流量控制,错峰流控,可以定义多种限流规则
- 灰度:线上灰度部署,可以减小风险
- 路由:动态路由规则
6 常用网关解决方案
6.1 nginx+lua
nginx是一个高性能http和反向代理服务器。nginx一方面可以做反向代理服务器,另一方面可以做静态资源服务器
- nginx适合做门户网关,是这个全局网关,对外处于最外层的那种;而gateway属于业务网关,主要来应对不同可短端提供服务,用于聚合业务。各个微服务独立部署,职责单一,对外提供无的时候需要有一个东西把业务聚合起来。
- gateway可以实现熔断、重试等功能。这个是nginx不具备的
6.2 kong
kong是mashape提供的一款api管理软件,他本身是基于nginx+lua的,但比nginx提供了更简单的配置方式,数据采用apacheCassandra/postgresql存储,并且提供了一些优秀的插件,如:验证,日志,调用频次限制等。kong非常诱人的地方就是提供了大量的插件来拓展应用,通过设置不同插件可以为服务提供各种增强的功能。
- 优点:预计nginx所以在性能和稳定性上都没有问题,kong作为一款商业软件,在nginx上做了很多拓展工作,而且还有付费的商业插件。kong本身也有付费的企业版,其中包括技术支持和培训以及api分析插件。
- 缺点:如果你使用了spring cloud ,kong如何结合目前已有的服务治理体系
6.3 Traefik
Traefik 是一个开源的go语言开发的为了让部署微服务更加便捷而诞生的现代HTTP反向代理,负载均衡工具。他支持多后台(docker、swarm、kubernetes,marathon、mesos。consoul、etcd、zookeeper)。来来自动化,动态奶的应用他的配置文件设置。traefik拥有一个机遇angularjs编写简单网站界面,支持restapi,配置文件热更新,无需重启进程。高可用的集群配置文件
- 相对于spring cloud 和kubernetes而言,目前比较适合kubernetes
6.4 spring cloud netflix zuul
zuul 是netflix公司开源的一个api 网关组件,spring cloud 对齐进行二次基于springboot的注解方式做到开箱即用,目前来说,结合spring cloud提供服务器治理体系,可以做到请求转发,更加配置或者默认路由规则进行路由和load balance,无缝集成hystrix。
- 退让可以通过自定义filter实现我们想要的功能,但是由于zuul本身设计是基于
单线程的接受请求和转发
是阻塞IO,不支持长链接。且springcloud 自己有gateway所以这个种方案,现在已经不怎么被市场接受了
6.5 spring cloud Gateway
7 nginx实现网关
这里不做重点,大家可以去查看我的博客,其原理就是做了一个反向代理。做地址正则配置,转发到不同的微服务上
7.1 路由工作原理
8 路由规则
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories官方文档
共计11个规则
8.1 after
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 山地时间(丹佛)之后提出的任何请求。
8.2 Before
所述Before
路线谓词工厂有一个参数,一个datetime
(其是Java ZonedDateTime
)。此谓词匹配发生在指定 之前的请求datetime
。以下示例配置了一个 before 路由谓词:
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 山地时间(丹佛)之前提出的任何请求。
8.3 Between
该Between
路线谓词工厂有两个参数,datetime1
并且datetime2
这是JavaZonedDateTime
对象。此谓词匹配发生在 之后datetime1
和之前的请求datetime2
。该datetime2
参数必须是后datetime1
。以下示例配置了一个 between 路由谓词:
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]
8.4 Cookie 路由谓词工厂
所述Cookie
路线谓词工厂采用两个参数,该cookiename
和regexp
(其是Java正则表达式)。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。以下示例配置 cookie 路由谓词工厂:
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
此路由匹配 2017 年 1 月 20 日 17:42 山地时间(丹佛)之后和 2017 年 1 月 21 日 17:42 山地时间(丹佛)之前提出的任何请求。这对于维护窗口可能很有用。
8.5 header
所述Header
路线谓词工厂采用两个参数,报头name
和一个regexp
(其是Java正则表达式)。此谓词与具有给定名称的标头匹配,其值与正则表达式匹配。以下示例配置标头路由谓词:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
如果请求的标头名称X-Request-Id
与\d+
正则表达式匹配(即,它的值为一位或多位数字),则此路由匹配。
8.6 HOST
该Host
路线谓词工厂需要一个参数:主机名的列表patterns
。该模式是一个 Ant 风格的模式,以.
作为分隔符。此谓词匹配Host
与模式匹配的标头。以下示例配置主机路由谓词:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
{sub}.myhost.org
还支持URI 模板变量(例如)。
如果请求具有这种路由匹配Host
用的头值www.somehost.org
或beta.somehost.org
或www.anotherhost.org
。
此谓词提取 URI 模板变量(例如sub
,在前面的示例中定义的)作为名称和值的映射,并将其放置在 中ServerWebExchange.getAttributes()
,键定义在 中ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
。然后这些值可供工厂使用GatewayFilter
8.7 Method
所述Method
路线谓词厂需要methods
的参数,它是一个或多个参数:HTTP方法来匹配。以下示例配置方法路由谓词:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
8.8 Path(常用)
该Path
路线谓词厂有两个参数:春天的列表PathMatcher
patterns
和所谓的可选标志matchTrailingSlash
(默认true
)。以下示例配置路径路由谓词:
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
8.9 Query
所述Query
路线谓词工厂采用两个参数:所要求的param
和可选的regexp
(其是Java正则表达式)。以下示例配置查询路由谓词:
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
如果请求包含green
查询参数,则前面的路由匹配。
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
如果请求包含red
其值与正则gree.
表达式匹配的查询参数,则前面的路由匹配,因此green
和greet
将匹配。
8.10 RemoteAddr
所述RemoteAddr
路线谓词工厂需要的列表(分钟尺寸1) sources
,其是CIDR的表示法(IPv4或IPv6)的字符串,如192.168.0.1/16
(其中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
。
8.11 Weight
该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
注意分组的概念。
8.12 java正则表达式
字符 | 说明 |
\ | 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如, n匹配字符 n。\n 匹配换行符。序列 \ 匹配 \ ,\( 匹配 (。 |
^ | 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与"\n"或"\r"之后的位置匹配。 |
$ | 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与"\n"或"\r"之前的位置匹配。 |
* | 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,}。 |
+ | 一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。 |
? | 零次或一次匹配前面的字符或子表达式。例如,"do(es)?“匹配"do"或"does"中的"do”。? 等效于 {0,1}。 |
{n} | n 是非负整数。正好匹配 n 次。例如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配。 |
{n,} | n 是非负整数。至少匹配 n 次。例如,"o{2,}“不匹配"Bob"中的"o”,而匹配"foooood"中的所有 o。"o{1,}“等效于"o+”。"o{0,}“等效于"o*”。 |
{n,m} | m 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}"匹配"fooooood"中的头三个 o。‘o{0,1}’ 等效于 ‘o?’。注意:您不能将空格插入逗号和数字之间。 |
? | 当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。例如,在字符串"oooo"中,"o+?“只匹配单个"o”,而"o+“匹配所有"o”。 |
. | 匹配除"\r\n"之外的任何单个字符。若要匹配包括"\r\n"在内的任意字符,请使用诸如"[\s\S]"之类的模式。 |
(pattern) | 匹配 pattern 并捕获该匹配的子表达式。可以使用 pattern… 属性从结果"匹配"集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用"(“或者”)"。 |
(?:pattern) | 匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用"or"字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 ‘industry|industries’ 更经济的表达式。 |
(?=pattern) | 执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,‘Windows (?=95|98|NT|2000)’ 匹配"Windows 2000"中的"Windows",但不匹配"Windows 3.1"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。 |
(?!pattern) | 执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 x 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,‘Windows (?!95|98|NT|2000)’ 匹配"Windows 3.1"中的 “Windows”,但不匹配"Windows 2000"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。 |
y|x | 匹配 y 或 xyz。例如,‘z|food’ 匹配"z"或"food"。’(z|f)ood’ 匹配"zood"或"food"。 |
[xyz] | 字符集。匹配包含的任一字符。例如,"[abc]“匹配"plain"中的"a”。 |
[^a-z] | 反向字符集。匹配未包含的任何字符。例如,"[^abc]“匹配"plain"中"p”,“l”,“i”,“n”。 |
[a-z] | 字符范围。匹配指定范围内的任何字符。例如,"[a-z]"匹配"a"到"z"范围内的任何小写字母。 |
[^x] | 反向范围字符。匹配不在指定的范围内的任何字符。例如,"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符。 |
\b | 匹配一个字边界,即字与空格间的位置。例如,“er\b"匹配"never"中的"er”,但不匹配"verb"中的"er"。 |
\B | 非字边界匹配。“er\B"匹配"verb"中的"er”,但不匹配"never"中的"er"。 |
\cx | 匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回车符。n 的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是"c"字符本身。 |
\d | 数字字符匹配。等效于 [0-9]。 |
\D | 非数字字符匹配。等效于 [^0-9]。 |
\f | 换页符匹配。等效于 \x0c 和 \cL。 |
\n | 换行符匹配。等效于 \x0a 和 \cJ。 |
\r | 匹配一个回车符。等效于 \x0d 和 \cM。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。 |
\S | 匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。 |
\t | 制表符匹配。与 \x09 和 \cI 等效。 |
\v | 垂直制表符匹配。与 \x0b 和 \cK 等效。 |
\w | 匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。 |
\W | 与任何非单词字符匹配。与"[^A-Za-z0-9_]"等效。 |
\xn | 匹配 n,此处的 num 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,"\x41"匹配"A"。"\x041"与"\x04"&"1"等效。允许在正则表达式中使用 ASCII 代码。 |
*num* | 匹配 num,此处的 n 是一个正整数。到捕获匹配的反向引用。例如,"(.)\1"匹配两个连续的相同字符。 |
*n* | 标识一个八进制转义码或反向引用。如果 *n* 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八进制数 (0-7),那么 nm 是八进制转义码。 |
*nm* | 标识一个八进制转义码或反向引用。如果 *nm* 前面至少有 nm 个捕获子表达式,那么 n 是反向引用。如果 *nm* 前面至少有 n 个捕获,则 m 是反向引用,后面跟有字符 nm。如果两种前面的情况都不存在,则 *nm* 匹配八进制值 n,其中 m 和 n 是八进制数字 (0-7)。 |
\nml | 当 m 是八进制数 (0-3),l 和 nml 是八进制数 (0-7) 时,匹配八进制转义码 n。 |
\un | 匹配 n,其中 9 动态路由(服务于发现路由规则)是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (©)。 |
10 过滤器
因为业务系统的复杂性建议,不适用默认规则,而是人工配置的方式,实际业务中,一般不适用动态路由
10.1 过滤器
路由过滤器允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。路由过滤器的范围是特定的路由。Spring Cloud Gateway 包含许多内置的 GatewayFilter 工厂。
10.2 全局过滤器
这里就不展开说了,官方文档都有共计31个网关过滤器
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
GlobalFilter
该GatewayFilter
接口具有与 相同的签名
10.3 自定义过滤器
。这些是有条件地应用于所有路由的特殊过滤器。全局过滤器一共9个
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters
10.3.1 自定义网关过滤器
10.3.2 自定义全局过滤器
@Bean
public GlobalFilter customFilter() {
return new CustomGlobalFilter();
}
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("custom global filter");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
10.3.3 统一鉴权
11 cors配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*" #配置允许的跨域域名
allowedMethods: #配置允许跨域的方法
- GET
配置跨域解决方案
12 配置访问日志
-Dreactor.netty.http.server.accessLogEnabled=true
要启用 Reactor Netty 访问日志,请设置它必须是 Java 系统属性,而不是 Spring Boot 属性.
*<?xml version="1.0" encoding="UTF-8"?>
<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->
<!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,
如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 debug:当此属性设置为true时,将打印出logback内部日志信息,
实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 定义日志文件 输入位置 -->
<property name="log_dir" value="D:/logs" />
<!-- 日志最大的历史 30天 -->
<property name="maxHistory" value="15"/>
<!-- ConsoleAppender 控制台输出日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 对日志进行格式化 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
</encoder>
</appender>
<!-- INFO级别日志 appender -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录INFO级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/info-log.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
<file>${log_dir}/access_log.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="accessLog" />
</appender>
<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
<appender-ref ref="async"/>
</logger>
<!-- root级别 DEBUG -->
<root level="INFO">
<!-- 控制台输出 -->
<appender-ref ref="accessLog" />
<!-- 文件输出 -->
<appender-ref ref="STDOUT" />
<appender-ref ref="INFO" />
</root>
</configuration>
。
logback.xml
13 开发指南
13.1 编写自定义路由谓词工厂
这些是编写网关的一些自定义组件的基本指南。
RoutePredicateFactory
为了编写路由谓词,您需要实现AbstractRoutePredicateFactory
. 有一个MyRoutePredicateFactory.java可以扩展的抽象类。
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {
public MyRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
// grab configuration from Config object
return exchange -> {
//grab the request
ServerHttpRequest request = exchange.getRequest();
//take information from the request to see if it
//matches configuration.
return matches(config, request);
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
13.2 编写自定义网关过滤器工厂
GatewayFilter
要编写一个GatewayFilterFactory
,您必须实现AbstractGatewayFilterFactory
. 您可以扩展一个名为PreGatewayFilterFactory.java. 以下示例显示了如何执行此操作:
public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {
public PreGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// grab configuration from Config object
return (exchange, chain) -> {
//If you want to build a "pre" filter you need to manipulate the
//request before calling chain.filter
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
//use builder to manipulate the request
return chain.filter(exchange.mutate().request(builder.build()).build());
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
PostGatewayFilterFactory.java
public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {
public PostGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// grab configuration from Config object
return (exchange, chain) -> {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
//Manipulate the response in some way
}));
};
}
public static class Config {
//Put the configuration properties for your filter here
}
13.3 自定义网关过滤器的命名规范
Something
例如,要引用SomethingGatewayFilterFactory
在配置文件中命名的过滤器,该过滤器必须位于名为GatewayFilterFactory
.
可以创建名称不带class AnotherThing
后缀的网关过滤器 ,例如AnotherThing
. 可以不是在配置文件中引用此过滤器。这
13.4 编写自定义全局过滤器
受支持的命名约定,并且可能会在未来版本中删除此语法。请更新过滤器名称以符合要求。GlobalFilter
要编写自定义全局过滤器,您必须实现@Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> exchange.getPrincipal()
.map(Principal::getName)
.defaultIfEmpty("Default User")
.map(userName -> {
//adds header to proxied request
exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", userName).build();
return exchange;
})
.flatMap(chain::filter);
}
@Bean
public GlobalFilter customGlobalPostFilter() {
return (exchange, chain) -> chain.filter(exchange)
.then(Mono.just(exchange))
.map(serverWebExchange -> {
//adds header to response
serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked": "It did not work");
return serverWebExchange;
})
.then();
}
接口。这将过滤器应用于所有请求。
以下示例分别显示了如何设置全局前置过滤器和后置过滤器: