前言本文介绍如何使用endless+fsontify实现Linux服务器热更新。本以为站点更新会像.net、java等一样方便,直接上传更新文件后会自动重启查看最新效果,但是在golang中,我们需要手动完成。一般部署步骤goweb在服务器的部署步骤,一般都是打包成二进制文件,部署在多台linux服务器上。可以配合lvs、Nginx作为反向代理使用,实现负载均衡,保证站点的高可用。问题:二进制文件在linux上启动后,不能直接替换,必须有手动重启的动作,更新起来很麻烦,而且可能不让程序员接触服务器。不友好。浏览了网上已有的解决方案后,总结了以下几种自动编译方式。有些框架提供了自动编译功能,比如gin、fresh、rizla。这些都需要go环境。原理是检测代码的变化,自动为你做gobulid之类的事情。这不适用于服务器。服务端不会安装go环境这种麻烦的东西,只适用于本地开发环境。容器使用docker等容器部署endless为了让服务不中断优雅重启,可以使用endless来代替标准库net/http的ListenAndServe。本文介绍第三种方案的做法。要实现idea站点的更新,首先要更新文件,然后关闭原来的进程,重新启动。我们使用程序来帮助我们实现这一点,设置一个watch.conf监控文件,应用fsontify监控文件变化,如果检测到变化,开始执行命令为新文件授权权限:chmod777[binname],然后通知当前进程:kill-1[pid]。最后,endless开始发挥作用,重新启动程序,并使用原来的请求上下文。大体概览后,下面对关键部分进行说明。watch.conf在项目下新建test.watch.conf,内容如下{"HttpPort":"8080","Ver":"1","DelaySecond":5,"BinName":"test"}HttpPort是我们站点Ver当前版本的监听端口DelaySecond延迟多少秒启动。为了防止项目中的其他资源上传成功,设置了一个缓冲时间,但一般只需要更新一个二进制文件即可。BinName你编译的二进制文件的名字,是你gobuild之后生成的。fsontify监控watch.confwatcher,err:=fsnotify.NewWatcher()iferr!=nil{simpleLog(err)}deferwatcher.Close()gofunc(){for{select{caseevent:=<-watcher.Events:simpleLog("监听事件event:",event,time.Now())//ifevent.Op&fsnotify.Write==fsnotify.Write{//simpleLog("检测到文件修改")//restartChan<-true//}ifevent.Op&fsnotify.Remove==fsnotify.Remove{simpleLog("检测到文件被删除")restartChan<-true}case<-watcher.Errors://fmt.Println("error:",err)}}}()err=watcher.Add(watchPath)iferr!=nil{simpleLog(err)log.Fatal(err)}<-make(chanbool)听fsnotify.Write或fsnotify.Remove,(见每个人上传文件的方法不一样,事件可能不一样),然后给频道restartChan发消息,说明可以更新了。执行命令{<-restartChansimpleLog("receivednotificationtorestartWeb")m:=loadConf()ifm==nil{simpleLog("confparsingfailed")}else{curVer=m.VerbinName:=m.BinName//延迟x秒后重启,以防某些文件还没有上传"获取最新进程pid",curPid)//授权文件simpleLog("chmod777",binName)exec.Command("chmod","777",binName).Run()//通知当前进程关闭simpleLog("kill-1",curPid)exec.Command("kill","-1",curPid).Run()simpleLog("Updatecompleted",curPid,binName)}}等待通道restartChan,当消息收到,表示命令可以启动;loadConf()读取.conf文件的结构体,序列化为struct;授权新的二进制文件exec.Command("chmod","777",binName).Run();获取当前进程的PID,最后执行exec.Command("kill","-1",curPid).Run()通知自己的进程。请注意,它是-1,而不是-9。它们是有区别的。-9杀掉进程,开始就起不来。endlessg:=gin.New()//输出pid和版本信息g.GET("/hello",func(c*gin.Context){pid:=strconv.Itoa(syscall.Getpid())simpleLog("/hello",pid)c.JSON(200,gin.H{"message":"HelloGinnew2!","ver":curVer,"pid":pid})})m:=loadConf()curVer=m.Vers:=endless.NewServer(":"+m.HttpPort,g)err:=s.ListenAndServe()iferr!=nil{simpleLog("servererr:%v",err)}simpleLog("Serveron"+m.HttpPort+"stopped")os.Exit(0)选择的web框架是gin,原来用的是iris,但是没用。当然,你不需要web框架,你可以使用mux.NewRouter()。linux上site目录汇总如下,一个testgo二进制文件和一个.conf监控文件。如果修改watch.conf后记录的日志有错误或建议,请指出,共同改进。项目已经上传到github,需要源码的同学点这里:https://github.com/imleaf/go_...
