type
status
date
slug
summary
tags
category
icon
password
AI summary
优雅停机之Spring Cloud Gateway
引言
我们的网关是基于
Spring Cloud Gateway
做的二次开发,采用ZooKeeper来做注册中心(spring-cloud-discovery-zookeeper)。用ZooKeeper做注册中心的主要考虑是实际使用情况和成本,可以和dubbo的注册中心复用的一套集群。本篇文章使用的Spring Cloud Gateway版本是2.2.6-RELEASE
和dubbo服务一样,
Spring Cloud Gateway
也需要考虑优雅停机。这里有2个优雅停机的对象:Spring Cloud Gateway
- upstream
本篇文章我们主要讨论upstream的优雅停机。涉及到服务发现的优雅停机的顺序一般为:
- 从注册中心下线
- 关闭服务端口
- 其他bean销毁、回收等动作
线上问题
我们来看看没做优雅停机的一个线上upstream应用发布时的报错:

增加一次重试?
我们首次解决这个问题的思路并不是优雅停机,而是想:线上环境至少两个实例,是不是可以通过一次重试,来优雅的解决这个问题。不过尝试了一下发现问题没这么简单,因为
Spring Cloud Gateway
的重试机制默认没有“记忆”,也就是它并不会记住上一次调用报错的实例信息,下一次调用还是有一定的概率调用到正在重启的实例,并且不管你增加多少次重试都无法彻底解决这个问题。upstream停机时优先下线
这里猜测报错的原因肯定是先upstream把端口关了,然后再从注册中心下线的。我们来找找相关的代码:


可以看到,
ZooKeeperAutoServiceRegistration
是有考虑优雅停机的。只不过将自身从注册中心下线的逻辑走了普通Bean的Destory流程(也就是上图里@PreDestory
),太靠后了。所以造成了web端口已经关闭,但是注册中心还未下线的情况。虽然上述的改法会导致
destory()
方法在停机时执行两次,但是stop()
方法里面有幂等处理,在原来@PreDestory
阶段调用的时候依然不影响,只是我们把stop的流程提前了而已我们验证了改完之后的停机流程是先从注册中心下线,再关闭web端口,但是报错仍旧出现了。
服务列表缓存
原来,
Spring Cloud Gateway
的服务列表并不是实时从注册中心读取的。上面的改动只是确保注册中心里对应的实例被下线了,但是缓存在Spring Cloud Gateway
本地的服务列表里依然还有那个已经被下线的实例。所以,
Spring Cloud Gateway
需要监听对应ZooKeeper节点的变化,失效掉本地缓存。至此,upstream在发布时就没有再出现过上述的报错了。问题总算得到了解决。
思考
不过仔细思考一下,这个方案还是存在一些问题的。当注册中心的推送存在一定的延迟或者网络延迟较大时,虽然此时服务已经从注册中心下线了,但是客户端的缓存还是没有及时更新,可能会影响客户端的调用。
dubbo server端优雅停机时,会等待全部client断开连接再关闭dubbo端口。因为dubbo服务端和客户端是通过tcp长连接通信的,并且维护了活跃通道。如果对于netty和
Spring Cloud Gateway
的底层机制有一个更好的了解,相信我也可以把它的停机流程改造的更加优雅。- Author:黑微狗
- URL:https://blog.hwgzhu.com/article/spring-cloud-gateway-graceful-shutdown
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts