让我们给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的日志比较清晰,77625的父进程接收 SIGHUP 后,fork了子进程77625,接收到子进程传递的 SIGHUP 后,等待已有连接处理完成后终止,完全符合上述介绍的流程。

5. 验证版本

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

go程序不停机重启的更多相关文章

  1. endless 如何实现不停机重启 Go 程序?

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/584 前几篇文章讲解了如何实现一个高效的 HTTP 服务,这次我们来 ...

  2. linux 程序失败自动重启

    最近写了一个spark streaming 程序,但是程序跑着跑着就报错了,而且不会自动重启,以下脚本实现了程序失败自动重启 基本原理:查看程序日志文件是否有ERROR或Exception字样,有说明 ...

  3. C#程序注销、重启、关机和锁定电脑

    一:截图 二:源代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; usi ...

  4. android程序崩溃后重启

    有时候由于测试不充分或者程序潜在的问题而导致程序异常崩溃,这个是令人无法接受的,在android中怎样捕获程序的异常崩溃,然后进行一些必要的处理或重新启动 应用这个问题困恼了我很久,今天终于解决了该问 ...

  5. 任务计划程序-Windows2008定时重启

    参考网站:https://www.cnblogs.com/yeyun/p/6209540.html Windows系统的任务计划程序,可以添加计划任务,设置任务开始时间及执行的间隔,实现应用的自动执行 ...

  6. 解决Android后台清理APP后,程序自动重启的问题

    最近解决了一个Android APP的bug,发现APP在被后台清理后,会自动重启.现象很奇怪,有的手机(HTC)后台清理后,程序会再次重启,而有的手机(小米)则不会.猜想可能是小米手机内部做了处理, ...

  7. java:如何让程序按要求自行重启?

    正文开始前的废话: 这里的程序即包括b/s的web application,也包括standalone的类c/s的java application.   为什么要自我重启?   场景1:分布式环境中, ...

  8. Qt之重启应用程序

    简介 今天分享的内容有些意思-如何重启一个应用程序.其实,有时候这是一个很重要的功能点,而且很人性化.易用性很好. 例如:切换用户.当某个用户登录成功之后,需要切换到其它账号,那么这时,你就知道它的重 ...

  9. 【Qt】Qt之重启应用程序【转】

    简介 今天分享的内容有些意思-如何重启一个应用程序.其实,有时候这是一个很重要的功能点,而且很人性化.易用性很好. 例如:切换用户.当某个用户登录成功之后,需要切换到其它账号,那么这时,你就知道它的重 ...

随机推荐

  1. redis分片集群安装部署

    redis分片集群安装与部署 分片集群的优势 高可用.且方便扩展. 数据分片,多节点提供服务,提高性能,数据提供冗余备份. 分片集群部署 只需更改配置文件 部署架构:6个节点,3主3从.数据集分为3片 ...

  2. 【UE4 C++】定时器 Timer 与事件绑定

    概念 定时执行操作,可执行一次,或循环执行直到手动终止 定时器在全局定时器管理器(FTimerManager 类型)中管理.全局定时器管理器存在于 游戏实例 对象上以及每个 场景 中 定时器需要绑定委 ...

  3. 【UE4 设计模式】装饰器模式 Decorator Pattern

    概述 描述 动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活.是一种对象结构型模式. 套路 抽象构件(Component) 具体构 ...

  4. 剑指offer:JZ9 用两个栈实现队列

    JZ9 用两个栈实现队列 描述 用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能. 队列中的元素为int类型.保证操作合法,即保 ...

  5. xshell几款绝佳配色方案

    NO.1 [mycolor] text(bold)=e9e9e9 magenta(bold)=ff00ff text=00ff80 white(bold)=fdf6e3 green=80ff00 re ...

  6. PromQL的简单使用

    PromQL的简单使用 一.背景 二.PromQL的数据类型 三.字面量 1.字符串字面量 2.浮点数字面量 四.时间序列选择器 1.即时向量选择器 1.组成部分 2.指标名称和匹配器的组合 3.匹配 ...

  7. 扩展spring data jpa的repository

    在我们编写代码的过程中,spring data jpa为我们的持久层提供的极大的方便,但有时spring data jpa提供的repository并不能完全满足我们开发的需求,因此就需要进行扩展.s ...

  8. CSP2021 翻车记

    DAY - INF 日常模拟赛被吊打,不知道为啥总是出一些小问题导致正解gg,成绩的话也就是中游吧,不过方差不小 DAY - 2 感冒了,头疼得很,签到题甚至也签到失败了,烦得很 DAY -1 全真体 ...

  9. 攻防世界 杂项15.János-the-Ripper

    下载附件并解压,我用的是WinHex打开,发现是PK开头,并且文件中包含一个flag.txt文件,应该就是我们所需要的flag. 把下载的附件改后缀为.zip,确实有我们需要的flag,打开后需要密码 ...

  10. 写一段java程序来执行linux命令

    摘要 在日常开发中,程序员需要经常查询服务器日志来排查问题和调试程序.如果是本地调试还好,但项目一旦发布到服务器上,每次查日志就很麻烦,而且日志量巨大,有时我们无法找到我们需要的信息.经常需要借助第三 ...