背景
这周出现了一个重复请求的问题,两个请求之间刚好间隔一分钟,但是由于缺乏日志并不能判断这个是用户点击导致的,还是Gateway出现重复请求导致的,趁着周末有时间,再深入了解一下。
由于SpringCloud Gateway是基于Spring Webflux的,大量采用了Reactor,相比传统观念上的 单刀直入
,理解上会更加麻烦一点。
单纯的去看代码本身其实是很低效的一个行为,所以去我给自己定了一个小目标,了解一下 Gateway
的重试是怎么实现的。
过程
初看其实挺简单的,Google一下,发现就是配置一下 RetryGatewayFilterFactory
中的几个参数,其实现也挺简单的,判断一下Response的stateCode,service状态码,以及异常是否符合(默认存在IO异常和超时异常)。
routes:
- id: nacos-provider
uri: lb://nacos-provider
predicates:
- Path=/nacos-provider/**
filters:
- StripPrefix=1
- name: Retry
args:
routeId: nacos-provider
retries: 3
series:
- SERVER_ERROR
statuses:
- OK
methods:
- GET
- POST
加上好,启动项目,curl,发现并没有重试,最终原因如下,但是过程还是比较曲折的
- 打开debug日志
- 日志太多啦—->不得不吐槽Nacos的日志
- 配置了好几回的logback都未生效
- debug发现nacos jar包有一份logback日志,覆盖掉
开始看日志,在
org.springframework.cloud.gateway.handler
中发现如下日志[
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@5dcf0772}, order = -2147483648],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@1b46392c}, order = -2147482648],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@421ead7e}, order = -1],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter@6826b70f}, order = 0],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.GatewayMetricsFilter@781dac73}, order = 0],
[[RewritePath /nacos-provider/?(?<remaining>.*) = '/${remaining}'], order = 1],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@c472300}, order = 10000],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter@446b64b3}, order = 10150],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.LoadBalancerServiceInstanceCookieFilter@35ac9ebd}, order = 10151],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@4df7d9ee}, order = 2147483646],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@56c0a61e}, order = 2147483647],
[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@5f6494c0}, order = 2147483647]
]
显然没有
RetryGatewayFilterFactory
中的Filter
, 开始陷入为什么环节- 是不是
RetryGatewayFilterFactory
由于自动装配没有注册上去,debug发现注册了 - 是不是没有加载进去,debug发现加载进去了
- 最终找到原因如下链接所示
于是把
spring.cloud.gateway.discovery.locator.enable=true
注释掉,再次curl成功了。但是具体原因我又没找到,于是开始找原因,上边链接的解释是enable=true
会自动加载注册中心的服务,然后建立一些默认的配置,找默认配置就得找到enable在哪里使用的,为此不得不把源代码拉下来, 网络悲催,一直下载不下来全部的配置。
顶着一片红找到了路由刷新的地方,Nacos默认每隔30秒发送一个HeartbeatEvent事件,springcloud收到事件后,又发送一个RefreshRoutesEvent事件,在CachingRouteLocator
和CachingRouteDefinitionLocator
中找到了消费的地方,由于是reactor代码,到这里就有点迷糊了,so bad。
具体原因请看这里: SpringCloud Gateway【 路由刷新】拓展
为此我又拓展了一个思考题,或者说是面试题,如果我去设计一个Gateway会怎么去思考,随便写写。
- 是不是
可用性
- 稳定
- 路由: 最重要的过程,收到URL,路由映射,转发到后端服务
- header路由
- path路由
- query路由
- cookie路由
- 等等
- 其他feature:
- 路由刷新
- 重试
- websocket
- 等等
- 性能
- 同步
- 异步
- 复杂性
- 满足可用性和性能的基础上,代码应该尽可能的简单,但又不能丢失拓展性,让更多的同学能够参与进来,群策群力。
- 拓展性