热重启的意义
- 可以让用户神不知鬼不觉的,更新后端的项目
- 测试的时候,也不用停止项目然后再开启项目,降低项目开启时间
golang热重启的主要步骤
golang热重启的思想
- 监听重启信号
- 收到信号之后,进行fork子进程,将服务监听的socket文件描述符传递给子进程
- 子进程监听父进程的socket,这个时候父进程和子进程都可以接收请求
- 子进程启动成功之后,父进程停止接收新的连接,等待旧连接处理完成(或超时)
- 父进程退出,重启完成
源码
package main
import (
"context"
"fmt"
"log"
"net"
"net/http"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
"flag"
)
var (
listener net.Listener
err error
server http.Server
graceful = flag.Bool("g", false, "listen on fd open 3 (internal use only)")
)
func init(){
log.SetFlags(log.Ldate | log.Lshortfile)
}
type MyHandler struct {
}
func (*MyHandler)ServeHTTP(w http.ResponseWriter, r *http.Request){
log.Println("request start at ", time.Now(), r.URL.Path+"?"+r.URL.RawQuery, "request done at ", time.Now(), " pid:", os.Getpid())
time.Sleep(10 * time.Second)
fmt.Fprintln(w,"cout")
log.Println("request done at ", time.Now(), " pid:", os.Getpid() )
log.Println(r.RemoteAddr)
}
func main() {
flag.Parse()
fmt.Println("start-up at " , time.Now(), *graceful)
if *graceful {
f := os.NewFile(3, "")
listener, err = net.FileListener(f)
fmt.Printf( "graceful-reborn %v %v %#v \n", f.Fd(), f.Name(), listener)
}else{
listener, err = net.Listen("tcp", ":8080")
}
server := http.Server{
Handler: &MyHandler{},
ReadTimeout: 6 * time.Second,
}
log.Printf("Actual pid is %d\n", syscall.Getpid())
if err != nil {
log.Println(err)
return
}
log.Printf(" listener: %v\n", listener)
go func(){
err := server.Serve(listener)
if err != nil {
log.Println(err)
}
}()
func(){
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT)
for{
sig := <- ch
log.Printf("signal: %v", sig)
ctx, _ := context.WithTimeout(context.Background(), 20*time.Second)
switch sig {
case syscall.SIGINT:
println("signal cause reloading")
signal.Stop(ch)
{
tl, ok := listener.(*net.TCPListener)
if !ok {
log.Println("listener is not tcp listener")
return
}
currentFD, err := tl.File()
if err != nil {
log.Println("acquiring listener file failed")
return
}
cmd := exec.Command(os.Args[0],"-g")
log.Println(cmd.Args)
cmd.ExtraFiles, cmd.Stdout,cmd.Stderr = []*os.File{currentFD} ,os.Stdout, os.Stderr
err = cmd.Start()
if err != nil {
log.Println("cmd.Start fail: ", err)
return
}
log.Println("forked new pid : ",cmd.Process.Pid)
}
server.Shutdown(ctx)
log.Println("graceful shutdown at ", time.Now())
}
}
}()
}
- flag.Bool好坑啊!你设置默认值为false,但是你写命令的时候,-g它的值就会变为true
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2019-06-16 go语言简单的执行shell命令