热升级

什么是热升级

see https://my.oschina.net/astaxie/blog/136364

我理解的热升级就是更新程序而不需要重启,当然这个重启是宏观上的重启,即不需要重新启动服务器或者服务进程,但从底层来讲,至少子进程还是需要重新启动一下的,毕竟更新了代码。

典型的热升级是nginx的 upgrade-on-fly

其原理就是启动两个master进程(一个新版本,一个旧版本),端口复用,同时处理请求,然后再下线旧版本的nginx,这里边要处理的就是保持连接的问题,即不能让旧版本nginx的连接断开,从而影响业务。

难点与要处理的问题

  • 保持连接(stop-gracefully) 不能断开old version与client建立的连接
  • 无缝衔接(zero-downtime)要保证新的请求都能被new version的binary所处理,不能遗漏或拒绝
  • 资源稳定(分布式资源锁)不能因为新旧版本的处理而导致资源不一致,资源抢占、竞争等问题

第一点和第二点是最基本要处理的,第三点则是业务层面,也就是在程序逻辑层面要保证的。

保持旧的连接同时转移新的请求给new version,就是要保证old version不退出的情况下,new version接管进程的listenner,golang不支持端口复用,所以只能有转移listenner的fd来实现。不管是master-worker类型的还是endless这种 fork-exec 类型的,都是这种实现。

现有的解决方案

我知道的开源实现方案有这么几个:

其中overseer和endless都是动手实践过的。

endless的缺点就是,old versino退出之后,新exec出来的进程是一个新的进程,也就是说,脱离的原来的环境而被init进程托管,这个时候如果你用systemd进行进程管理的话,systemd就会认为其已经dead了。

所以如果想采用endless的话,就要像nginx那样,用fork的方式启动进程,即启动之后fork一份,然后主进程退出,同时维护一份pid文件,告诉systemd这样的进程管理工具,他的pid是谁,还活着没。

而overseer则属于master-child这种类型,一个master负责检查是否有更新,有的话就新fork一个进程出来,转移所有的env,listenner,args等。上层来看,程序还是那个程序,但是负责处理请求的进程则换成了新的child进程。

两者相同的地方就是对于请求的处理,都在accept层面放了计数器(不然就没法知道还有多少个连接了)从而实现graceful stop。

然后

思来想去,还是overseer比较适合,相当于在上层加了一个handler,然后我给加了一个file的fetcher:

https://github.com/wrfly/overseer

看来大家都很忙,owner说是要给瞅瞅,但这么久了那个PR还没合。 当然,我也不介意别人用我的namespace hhh

参考资料

comments powered by Disqus