资讯专栏INFORMATION COLUMN

从PHP迁移至Golang - 热更新篇

i_garfileo / 1165人阅读

摘要:上篇大致提到的的热更新,本篇将详细论述。软件的热更新就是指在保持系统正常运行的情况下对系统进行更新升级。同样地,热更新也可以采取类似的处理。注包方式的热更新本文暂不讨论。

上篇大致提到的Golang的热更新,本篇将详细论述。

1、什么是热更新

网络上有这么一个例子来形容热更新,我觉得很形象很贴切:

</>复制代码

  1. 一架行驶在高速上的大卡车,行驶过程中突然遭遇爆胎,热更新则是要求在不停车的情况下将车胎修补好,且补胎过程中卡车需要保持正常行驶。

软件的热更新就是指在保持系统正常运行的情况下对系统进行更新升级。常见的情况有:系统服务升级、修复现有逻辑、服务配置更新等。

2、热更新原理

先来看下Nginx热更新是如何做的?
Nginx支持运行中接收信号,方便开发者控制进程。

1)首先备份原有的Nginx二进制文件,并用新编译好的Nginx二进制文件替换旧的

2)然后向master进程发送USR2信号。此时Nginx进程会启动一个新版本Nginx,该新版本Nginx进程会发起一个新的master进程与work进程。即此时会有两个Nginx实例在运行,一起处理新来的请求。

3)再向原master进程发送WINCH信号,它会逐渐关闭相关work进程,此时原master进程仍保持监听新请求但不会发送至其下work进程,而是交给新的work进程

4)最后等到所有原work进程全部关闭,向原master进程发送QUIT信号,终止原master进程,至此,完成Nginx热升级。

:在*nix系统中,信号(Signal)是一种进程间通信机制,它给应用程序提供一种异步的软件中断,使应用程序有机会接受其他程序或终端发送的命令(即信号)。

同样地,Golang热更新也可以采取类似的处理。如上篇所述,都是利用用户自定义信号USR2

:Plugin包方式的Golang热更新本文暂不讨论。

3、热更新实现

Golang热更新可以细分为服务热『更新』(即热升级,类比Nginx的restart命令)与配置文件热更新(类比Nginx的reload命令)。接下来从实现细节处依次讨论。

3.1 服务热更新

大致流程如下:

1)Golang服务进程运行时监听USR2信号

2)进程收到USR2信号后,fork子进程(启动新版本服务),并将当前socket句柄等进程环境交给它

3)新进程开始监听socket请求

4)等待旧服务连接停止

主要代码示例如下:
监听USR2信号

</>复制代码

  1. func (a *app) signalHandler(wg *sync.WaitGroup) {
  2. ch := make(chan os.Signal, 10)
  3. signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
  4. for {
  5. sig := <-ch
  6. switch sig {
  7. case syscall.SIGINT, syscall.SIGTERM:
  8. // 确保接收到INT/TERM信号时可以触发Golang标准的进程终止行为
  9. signal.Stop(ch)
  10. a.term(wg)
  11. return
  12. case syscall.SIGUSR2:
  13. err := a.preStartProcess()
  14. if err != nil {
  15. a.errors <- err
  16. }
  17. // 发起新进程
  18. if _, err := a.net.StartProcess(); err != nil {
  19. a.errors <- err
  20. }
  21. }
  22. }
  23. }

复制当前进程socket连接,发起新进程

</>复制代码

  1. execSpec := &syscall.ProcAttr{
  2. Env: os.Environ(),
  3. Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
  4. }
  5. fork, err := syscall.ForkExec(os.Args[0], os.Args, execSpec)
  6. ...

详细源码可见:https://scalingo.com/articles...

以上仅为代码示例,目前已经成熟的开源实现主要有:endless和facebook的grace,原理基本类似,fork一个子进程,子进程监听原有父进程socket端口,父进程优雅退出。

在实际的生产环境中推荐使用以上开源库,关于热更新开源库的使用非常方便,下面是facebook的grace库的例子:
引入github.com/facebookgo/grace/gracehttp

</>复制代码

  1. func main() {
  2. app := gin.New()// 项目中时候的是gin框架
  3. router.Route(app)
  4. var server *http.Server
  5. server = &http.Server{
  6. Addr: ":8080",
  7. Handler: app,
  8. }
  9. gracehttp.Serve(server)
  10. }

利用go build命令编译,生成服务的可执行文件。
然后再用shell封装一下服务命令,生成restat.sh命令文件

</>复制代码

  1. #!/bin/sh
  2. ps aux | grep wingo
  3. count=`ps -ef | grep "wingo" | grep -v "grep" | wc -l`
  4. echo ""
  5. if [ 0 == $count ]; then
  6. echo "Wingo starting..."
  7. sudo ./wingo &
  8. echo "Wingo started"
  9. else
  10. echo "Wingo Restarting..."
  11. sudo kill -USR2 $(ps -ef | grep "wingo" | grep -v grep | awk "{print $2}")
  12. echo "Wingo Restarted"
  13. fi
  14. sleep 1
  15. ps aux | grep wingo

:其中wingo为服务的二进制名称。

于是,便可通过执行./restart.sh命令,达到对服务的热升级目的。

3.2 配置文件热更新

配置文件热更新是指在不停止服务的情况下,重新加载服务所有配置文件。
与3.1服务热升级原理一样,利用用户自定义信号:USR1,即可实现服务的配置文件热更新。

1)服务监听USR1信号

2)服务接收到USR1信号后,停止接受新的连接,等待当前连接停止,重新载入配置文件,重启服务器,从而实现相对平滑的不停服的更改。

主要代码实现:

</>复制代码

  1. // LoadAllConf 调用加载配置文件函数
  2. // load为具体加载配置文件方法
  3. func LoadAllConf(load func(bool)) {
  4. load(true)
  5. listenSIGUSR1(load)
  6. }
  7. // listenSIGUSR1 监听SIGUSR1信号
  8. func listenSIGUSR1(f func(bool)) {
  9. s := make(chan os.Signal, 1)
  10. signal.Notify(s, syscall.SIGUSR1)
  11. go func() {
  12. for {
  13. <-s
  14. f(false)
  15. log.Println("Reloaded")
  16. }
  17. }()
  18. }

详细源码可见:https://www.openmymind.net/Go...

利用go build命令编译,生成服务的可执行文件。
然后再用shell封装一下配置重载命令,生成reload.sh命令文件

</>复制代码

  1. #!/bin/sh
  2. ps aux | grep wingo
  3. echo ""
  4. echo "Wingo Reloading..."
  5. sudo kill -USR1 $(ps -ef | grep "wingo" | grep -v grep | awk "{print $2}")
  6. echo "Wingo Reloaded"
  7. echo ""
  8. sleep 1
  9. ps aux | grep wingo

于是,便可通过执行./reload.sh命令,达到对服务的配置文件热升级目的。

4、总结

本文主要描述了Golang服务热升级与配置文件热更新原理与主要代码实现,本质上也不是什么新内容,如果之前读过《Unix环境高级编程》,就会觉得很亲切。底层原理基本上是利用了信号这个软件中断机制,在运行中改变常驻进程的行为。

References

https://scalingo.com/articles...
http://kuangchanglang.com/gol...
https://blog.csdn.net/black_O...
https://www.openmymind.net/Go...
https://blog.csdn.net/qq_1543...
https://wrfly.kfd.me/posts/%E...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/29722.html

相关文章

  • PHP迁移Golang - 基础

    摘要:但是,随着微服务架构的兴起以及容器时代的到来,这种情况又再次加剧。最重要的是,在微服务与容器领域有很好的基础,后期系统可完美实现微服务化与容器化。 1、Why Not PHP 使用PHP构建的WEB程序,随着业务发展到一定体量之后,都不得不面临以下一些问题: 业务功能不断扩张,如何避免某单一业务功能故障影响整体,维持系统健壮性 业务逻辑复杂度不断上升,如何解耦与模块化,降低系统复杂性...

    Kerr1Gan 评论0 收藏0
  • 途牛原创|途牛周刊

    摘要:的本质是团队博客,关注互联网创业技术,每周推荐篇优质文章。坚持争取做到每周更新,与读者一起进步。第十一期第十期第九期第八期第七期第六期第五期第四期第三期切换至,第二期发布。创刊,用发布了第一次。 Tuniu Weekly Inspired By 《湾区日报》 我们团队也想基于这种模式,让大家感受到技术的人文。 《Tuniu Weekly》就这样产生了。 《Tuniu Weekly》...

    ThreeWords 评论0 收藏0
  • [开源] LaravelPlus - 基于 Laravel 魔改,为方便实际业务使用 - 开发中

    摘要:目的为了减少重复和新项目的配置麻烦等问题,就是为了骗星星如现有的生成工具虽然好用,但是不太喜欢样式和代码结构。有些本地,测试,线上的配置需要频繁改动的需要。 目的 为了减少重复 CURD 和新项目的配置麻烦等问题,(就是为了骗星星:LaravelPlus )如: 现有的 infyomlabs/laravel-generator CODE 生成工具虽然好用,但是不太喜欢样式和代码结构。...

    weknow619 评论0 收藏0
  • TiDB 助力东南亚领先电商 Shopee 业务升级

    摘要:作者介绍刘春辉,洪超,一业务场景是东南亚和台湾地区领先的电子商务平台,覆盖新加坡马来西亚菲律宾印度尼西亚泰国越南和台湾等七个市场。母公司为首家在纽约证券交易所上市的东南亚互联网企业。 作者介绍刘春辉,Shopee DBA洪超,Shopee DBA 一、业务场景 Shopee(https://shopee.com/)是东南亚和台湾地区领先的电子商务平台,覆盖新加坡、马来西亚、菲律宾、印...

    hoohack 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<