go程序不停机重启
<p>让我们给http服务写一个版本更新接口,让它自动更新版本并重启服务吧。</p><p> </p>
<p><span style="color: rgba(51, 51, 51, 1)"><span style="font-size: 18px"><strong>初步例子</strong></span><br></span></p>
<p><span style="color: rgba(128, 128, 128, 1); font-size: 13px"> <strong>注:为了精简,文中代码都去除了err处理</strong> </span></p>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(51, 51, 51, 1); font-size: 15px"><strong>main.go</strong></span></p>
<pre class="language-go"><code>var Version = "1.0"
/* 打印版本 */
func version(w http.ResponseWriter, r *http.Request) {
msg := fmt.Sprintf("version %v\n", Version)
w.Write([]byte(msg))
}
/* 版本升级 */
func upgrade(w http.ResponseWriter, r *http.Request) {
// 1. 把新版本文件放置到服务主目录(简化)
os.Remove("test_restart")
os.Rename("new_test_restart", "test_restart")
// 2. go的可执行文件加权限(省略)
// 3. 重启服务
cmd := exec.Command("/bin/bash", "-c", "./restart.sh")
cmd.Output()
w.Write([]byte("restart ok\n"))
}
func main() {
// 记录pid
f,_ := os.Create("s.pid")
pid := os.Getpid()
f.WriteString(fmt.Sprintf("%v", pid))
fmt.Printf("System running:%v\n", pid)
// 监听连接
mux := http.NewServeMux()
mux.HandleFunc("/version", version)
mux.HandleFunc("/upgrade", upgrade)
http.ListenAndServe("127.0.0.1:9527", mux)
}</code></pre>
<p><span style="font-size: 15px"><strong>restart.sh</strong></span>(重启脚本)</p>
<div class="cnblogs_code">
<pre class="language-bash"><code>kill -9 $(cat s.pid)
nohup ./test_restart > nohup.log 2>&1 &</code></pre>
</div>
<p> </p>
<p><span style="font-size: 18px"><strong> 测试</strong></span></p>
<p> 1. 编译后开始运行</p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211122180938843-393946936.png"></p>
<p> 2. 请求一下版本信息接口</p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211123100726675-259567910.png"></p>
<p> 3. 将代码中Version改为“1.1”, 生成一个新文件“new_test_restart”</p>
<p> 4. 请求版本更新接口</p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211123160854269-1320150230.png"></p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211122183654846-551133098.png"></p>
<p><span style="font-size: 18px"><strong>发现问题</strong></span></p>
<p> 重启脚本是stop后start的,stop时直接杀死了进程,程序直接中断了当前所有的连接,此时接口函数还未return,导致调用方接收不到响应。</p>
<p> </p>
<p><span style="font-size: 18px"><strong>使用<span style="color: rgba(255, 102, 0, 1)">Endless</span></strong></span></p>
<p> 看来得不中断已有连接的情况下进行重启才行,不能简单的stop后start,得平滑重启。大致就是让父进程启动一个子进程去监听新的连接,自己不再监听新的连接,而是在处理完已有连接后终止,之后子进程独挑大梁。</p>
<p> 随后发现github上的<span style="color: rgba(255, 102, 0, 1)">endless</span>挺满足需求,它是一个不停机重启的服务器实现,实现流程为:</p>
<ol>
<li><strong><span style="font-size: 15px">监听 SIGHUP 信号</span></strong></li>
<li><strong><span style="font-size: 15px">收到信号后 fork 子进程(使用相同的启动命令),将服务监听的 socket 文件描述符传递给子进程</span></strong></li>
<li><strong><span style="font-size: 15px">子进程启动成功后开始监听新的连接,并发送 SIGTERM 信号给父进程</span></strong></li>
<li><strong><span style="font-size: 15px">父进程收到 SIGTERM 信号后停止接收新的连接,等待旧连接处理完成后终止</span></strong></li>
<li><strong><span style="font-size: 15px">父进程终止,重启完成</span></strong></li>
</ol>
<p> 关于 <strong>SIGHUP </strong>信号,我们可以用“kill -1”命令发送给endless。</p>
<p> 使用endless改动很小,在main函数中只需要把 http.ListenAndServe 修改为 endless.ListenAndServe即可: </p>
<p><span style="font-size: 15px"><strong>main.go</strong></span></p>
<div class="cnblogs_code">
<pre class="language-go"><code>func main() {
// 记录pid(省略)
// 监听连接
mux := http.NewServeMux()
mux.HandleFunc("/version", version)
mux.HandleFunc("/upgrade", upgrade)
// http.ListenAndServe("127.0.0.1:9527", mux)
endless.ListenAndServe("127.0.0.1:9527", mux)
}</code></pre>
</div>
<p><span style="font-size: 15px"><strong>restart.sh</strong></span></p>
<pre class="language-bash"><code>kill -1 $(cat s.pid)</code></pre>
<p> </p>
<p><span style="font-size: 18px"><strong>再测试</strong></span></p>
<p> 1. 编译后开始运行(带时间前缀的是endless打印的日志)</p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211123161551396-567113622.png"></p>
<p> 2. 请求一下版本信息接口</p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211123100726675-259567910.png"></p>
<p> 3. 将代码中Version改为“1.1”, 生成一个新文件“new_test_restart”</p>
<p> 4. 请求版本更新接口</p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211123101803126-834901207.png"></p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211123161637962-1656069349.png"></p>
<p><span style="font-size: 15px"> </span><span style="font-size: 15px">请求没有被中断,成功接收了响应。</span></p>
<p><span style="font-size: 15px"> endless的日志比较清晰,105300的父进程接收 <strong>SIGHUP </strong>后,fork了子进程105335,接收到子进程传递的 <strong>SIGHUP </strong>后,等待已有连接处理完成后终止,完全符合上述介绍的流程。</span></p>
<p> 5. 验证版本</p>
<p> <img src="https://img2020.cnblogs.com/blog/1259514/202111/1259514-20211123102617035-299451070.png"></p>
<p> <span style="font-size: 15px"> <span style="font-size: 16px"><strong>至此</strong>,</span></span><strong><span style="font-size: 15px"><span style="font-size: 16px">不停机版本更新成功</span> <span style="font-size: 16px">٩(◕‿◕。)۶</span></span></strong></p>
<p> </p><br><br>
来源:https://www.cnblogs.com/MilletChili/p/15593756.html
頁:
[1]