36

适用线上服务器的go web自动重启的更新方案

 5 years ago
source link: https://studygolang.com/articles/29968
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前言

本文阐述如何使用endless+fsontify实现linux服务器上的热更新。原以为站点更新会像.net、java等那么方便,直接上传更新文件就会自动重启看到最新效果,但在golang中,需要我们手动来实现。

常规部署

步骤

go web在服务器上的部署步骤一般是打包成二进制文件部署在多台Linux服务器上,可搭配lvs、Nginx来做反向代理,实现负载均衡,保障站点的高可用。

问题

linux上二进制文件启动后,无法直接替换,得有个手动重启的动作,这个对更新来说就非常麻烦了,而且程序员可能还不允许接触服务器,得通过运维同学来协助,这明显不友好。

现有解决方案

在网上浏览一番后,我总结了以下几种方式

  1. 自动编译 有一些框架提供了自动编译的功能,例如 gin、fresh、rizla。这些都需要go环境,原理是检测到code有变化,自动帮你做类似go bulid的事,这并不适合服务器,服务器上不会去装go环境这么麻烦的东西,仅适用本地开发环境。
  2. 容器 使用docker等容器来部署
  3. endless 为了让服务不中断,优雅的进行重启,可以利用 endless 来替换标准库net/http的ListenAndServe

本文就阐述第3种方案的实践。

实现

思路

站点的更新,首先要更新文件,接着要关闭原进程,重新启动。我们就用程序来帮我们实现这件事情,设定一个watch.conf的监控文件,应用fsontify来监听文件的变更,如果检测到变更,开始执行命令,授权新文件的权限:chmod 777 [binname],接着通知当前进程:kill -1 [pid]。最后endless发挥作用,会把程序重新启动,并且使用的是原来的请求上下文。笼统的概括完之后,下面讲解下关键部分。

watch.conf

在项目下新建test.watch.conf,内容如下

{
    "HttpPort": "8080",
    "Ver": "1",
    "DelaySecond":  5,
    "BinName":  "test"
}
  • HttpPort 是我们站点的监听端口
  • Ver 当前版本
  • DelaySecond 延迟多少秒启动,为了避免项目里有其他的资源没有上传成功,设定一个缓冲的时间,但一般就一个二进制文件要更新而已。
  • BinName 你编译的二进制文件名,就是你go build后生成的。

fsontify监听watch.conf

watcher, err := fsnotify.NewWatcher()
    if err != nil {
        simpleLog(err)
    }
    defer watcher.Close()

    go func() {
        for {
            select {
            case event := <-watcher.Events:
                simpleLog("监听到事件event:", event, time.Now())
                //if event.Op&fsnotify.Write == fsnotify.Write {
                //    simpleLog("检测到文件修改")
                //    restartChan <- true
                //}

                if event.Op&fsnotify.Remove == fsnotify.Remove {
                    simpleLog("检测到文件被删除")
                    restartChan <- true
                }
            case <-watcher.Errors:
                //fmt.Println("error:", err)
            }
        }
    }()

    err = watcher.Add(watchPath)
    if err != nil {
        simpleLog(err)
        log.Fatal(err)
    }

    <-make(chan bool)

监听到fsnotify.Write或者fsnotify.Remove,(看大家使用的上传文件方式,方式不同,事件可能不一样),然后给通道restarChan发送消息,表示可以更新了。

执行命令

for {
        <-restartChan
        simpleLog("收到通知 准备 restartWeb")

        m := loadConf()
        if m == nil {
            simpleLog("conf 解析失败")
        } else {
            curVer = m.Ver
            binName := m.BinName

            //延时x秒后再执行重启,以防有的文件还没上传完成
            time.Sleep(time.Duration(m.DelaySecond) * time.Second)

            curPid = strconv.Itoa(syscall.Getpid())
            simpleLog("获取最新进程pid", curPid)

            //给文件授权
            simpleLog("chmod 777", binName)
            exec.Command("chmod", "777", binName).Run()

            //通知当前进程关闭
            simpleLog("kill -1", curPid)
            exec.Command("kill", "-1", curPid).Run()

            simpleLog("更新完成", curPid, binName)
        }
    }

等待通道 restarChan ,当收到消息,表示可以开始执行命令了; loadConf() 读取.conf文件的结构,序列化到struct;给新的二进制文件授权 exec.Command("chmod", "777", binName).Run() ;获取当前的进程PID,最后执行 exec.Command("kill", "-1", curPid).Run() 通知自己的进程,注意是-1,不是-9,有差别,-9就杀死进程,启动不起来的。

endless

g := gin.New()

    //输出pid和版本信息
    g.GET("/hello", func(c *gin.Context) {
        pid := strconv.Itoa(syscall.Getpid())
        simpleLog("/hello", pid)
        c.JSON(200, gin.H{"message": "Hello Gin new2!", "ver": curVer, "pid": pid})
    })

    m := loadConf()
    curVer = m.Ver
    s := endless.NewServer(":"+m.HttpPort, g)
    err := s.ListenAndServe()
    if err != nil {
        simpleLog("server err: %v", err)
    }

    simpleLog("Server on " + m.HttpPort + " stopped")

    os.Exit(0)

选用的web框架是gin,原先用的是iris,发现不行。当然不用web框架,用 mux.NewRouter() 也可以的。

总结

linux上的站点目录如下,一个test go的二进制文件,一个.conf监控文件。

Vf6Zz2z.png!web

变更watch.conf后记录的日志

JJvEvyr.png!web

如果有任何不对的或者建议,欢迎指出,共同改进。

项目传到github上了,需要源码的同学戳这里: https://github.com/imleaf/go_...

有疑问加站长微信联系

iiUfA3j.png!web

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK