

适用线上服务器的go web自动重启的更新方案
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.

前言
本文阐述如何使用endless+fsontify实现linux服务器上的热更新。原以为站点更新会像.net、java等那么方便,直接上传更新文件就会自动重启看到最新效果,但在golang中,需要我们手动来实现。
常规部署
步骤
go web在服务器上的部署步骤一般是打包成二进制文件部署在多台Linux服务器上,可搭配lvs、Nginx来做反向代理,实现负载均衡,保障站点的高可用。
问题
linux上二进制文件启动后,无法直接替换,得有个手动重启的动作,这个对更新来说就非常麻烦了,而且程序员可能还不允许接触服务器,得通过运维同学来协助,这明显不友好。
现有解决方案
在网上浏览一番后,我总结了以下几种方式
- 自动编译 有一些框架提供了自动编译的功能,例如 gin、fresh、rizla。这些都需要go环境,原理是检测到code有变化,自动帮你做类似go bulid的事,这并不适合服务器,服务器上不会去装go环境这么麻烦的东西,仅适用本地开发环境。
- 容器 使用docker等容器来部署
- 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监控文件。
变更watch.conf后记录的日志
如果有任何不对的或者建议,欢迎指出,共同改进。
项目传到github上了,需要源码的同学戳这里: https://github.com/imleaf/go_...
有疑问加站长微信联系

Recommend
-
35
阿里妹导读: 本文从线上实际问题和故障的排查出发,分享如何快速定位和恢复线上常见问题和故障,总结了很多实...
-
12
一、前言 热更新代码的场景 1)当线上服务器出现问题时,有些时候现有的手段不足以发现问题所在,可...
-
6
Mac 重启后自动 ssh-add 私钥 管理服务器有一些安全常识: 禁止 root 远程登录 禁止密码登录 使用 priviate / public key 作为默认的登录方式。 修改 sshd 端口 From Linode:
-
21
shell脚本监控进程挂掉自动重启2019年7月12日 by anzhihe·0评论 · 957 人阅读 · 隐藏边栏
-
24
.NET Core,.NET5默认配置都是只加载一次,修改配置时都需要重启才能生效,如何能修改即时生效呢,下面来演示一遍。 一、设置配置文件实时生效 1.1配置 在Program.cs的CreateHostBuilder()处增加加载配置文件的时候...
-
3
V2EX › 程序员 edge 重启后意外的把 chrome 的插件都自动装上了,有彦祖遇到过吗? wa...
-
8
打不死的小强,让Docker的容器自动重启 | 南瓜慢说官方网站技术之前,先读诗书:回风动地起,秋草萋已绿。 打不死的小...
-
4
新版Linux将实现无需重启下进行BIOS更新,暂时为服务器独有功能
-
7
V2EX › Windows 求助, Win10 更新后自动重启如何关闭?
-
11
禁止Windows Server自动更新及更新后自动重启 精选 原创 Windows Server等服务器,...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK