• go程序不停机重启


    让我们给http服务写一个版本更新接口,让它自动更新版本并重启服务吧。

    初步例子

      注:为了精简,文中代码都去除了err处理  

    main.go

    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)
    }

    restart.sh(重启脚本)

    kill -9 $(cat s.pid)
    nohup ./test_restart > nohup.log 2>&1 &

     测试

        1. 编译后开始运行

         

        2. 请求一下版本信息接口

       

        3. 将代码中Version改为“1.1”, 生成一个新文件“new_test_restart”

        4. 请求版本更新接口

       

       

    发现问题

        重启脚本是stop后start的,stop时直接杀死了进程,程序直接中断了当前所有的连接,此时接口函数还未return,导致调用方接收不到响应。

    使用Endless

        看来得不中断已有连接的情况下进行重启才行,不能简单的stop后start,得平滑重启。大致就是让父进程启动一个子进程去监听新的连接,自己不再监听新的连接,而是在处理完已有连接后终止,之后子进程独挑大梁。

        随后发现github上的endless挺满足需求,它是一个不停机重启的服务器实现,实现流程为:

    1. 监听 SIGHUP 信号
    2. 收到信号后 fork 子进程(使用相同的启动命令),将服务监听的 socket 文件描述符传递给子进程
    3. 子进程启动成功后开始监听新的连接,并发送 SIGTERM 信号给父进程
    4. 父进程收到 SIGTERM 信号后停止接收新的连接,等待旧连接处理完成后终止
    5. 父进程终止,重启完成

        关于 SIGHUP 信号,我们可以用“kill -1”命令发送给endless。

        使用endless改动很小,在main函数中只需要把 http.ListenAndServe 修改为 endless.ListenAndServe即可: 

    main.go

    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)
    }

    restart.sh

    kill -1 $(cat s.pid)

    再测试

       1. 编译后开始运行(带时间前缀的是endless打印的日志)

       

        2. 请求一下版本信息接口

       

        3. 将代码中Version改为“1.1”, 生成一个新文件“new_test_restart”

        4. 请求版本更新接口

       

       

            请求没有被中断,成功接收了响应。

            endless的日志比较清晰,105300的父进程接收 SIGHUP 后,fork了子进程105335,接收到子进程传递的 SIGHUP 后,等待已有连接处理完成后终止,完全符合上述介绍的流程。

        5. 验证版本

       

        至此不停机版本更新成功 ٩(◕‿◕。)۶

  • 相关阅读:
    汇编结合vc6的使用
    QT textbroswer textedite Qlist的常用的操作函数
    QT Qdialog的对话框模式以及其关闭
    QT生成的exe在其他电脑打开
    c++实现服务器和多个客户端的实时群聊通信
    c++ 实时通信系统(基础知识TCP/IP篇)
    c++的并发操作(多线程)
    六种Socket I/O模型幽默讲解
    c++字符串的输入
    字符串 与其他数据类型的转换,以及字符创的常用操作
  • 原文地址:https://www.cnblogs.com/MilletChili/p/15593756.html
Copyright © 2020-2023  润新知