热升级如何不断开旧连接

现在有一个tcp长连接服务器,单机,想实现热升级


网上有方法可以将server的fd文件句柄通过环境变量传递给新启动的子进程。


但是有个问题,新进程启动后,新连接没问题,旧连接怎么转移?只能断开重连吗?


参考的这篇文章:


https://www.oschina.net/translate/graceful-server-restart-with-go


使用的这个现成的库:


github.com/facebookgo/grace/gracenet


然后这是我的测试代码,老进程退出后客户端就断开连接了:


package main

import (
"github.com/facebookgo/grace/gracenet"
"os"
"os/signal"
"syscall"
"fmt"
"net"
"time"
)

func main() {
net := gracenet.Net{}
l,err := net.Listen("tcp",":12345")
if err != nil {
panic(err)
}
go func() {
for {
conn,err := l.Accept()
if err != nil {
fmt.Println("accept 失败",err)
return
}
go handleConn(conn)
}
}()

fmt.Println("程序启动..")

ch := make(chan os.Signal, 10)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
for {
sig := <-ch
switch sig {
case syscall.SIGINT, syscall.SIGTERM:
os.Exit(0)
case syscall.SIGUSR2:
//启动新线程,超时关闭当前线程
pid,err := net.StartProcess()
if err != nil {
fmt.Println("新进程启动失败",err)
os.Exit(1)
}
fmt.Println("启动新进程...,pid=",pid)
//停止accept
l.Close()
time.Sleep(15*time.Second)
fmt.Println("老进程退出")
os.Exit(0)
}
}
}

func handleConn(conn net.Conn) {
buf := make([]byte, 1024)
fmt.Println("client connected")
for {
n,err := conn.Read(buf)
if err != nil {
fmt.Println("client disconnected")
return
}
conn.Write(buf[0:n])
conn.Write([]byte("ok"))
}
}
已邀请:

lrita

赞同来自:

等go module功能完善了,把业务代码放到module里,到时reload module

whisper219

赞同来自:

老进程等通讯完毕后断开连接

lichao2018

赞同来自:

linux上fd是可以跨进程转移的,参考sendmsg、recvmsg, SCM_RIGHTS的用法

chyjlu

赞同来自:

我前几天看的这几个文章 不知道对你有没帮助。好像go1.8以上server可以优雅关闭的
https://grisha.org/blog/2014/06/03/graceful-restart-in-golang/
http://kuangchanglang.com/golang/2017/04/27/golang-graceful-restart
https://github.com/facebookgo/grace

geemo

赞同来自:

逻辑方法挂载在某个 struct 上这种逻辑是不可以热更新的,除非你逻辑用 plugin 实现无状态逻辑,


长连接的热升级,可以把连接的 fd 继承到新进程,继承过程中可能会有一部分已经传输的数据还在网络设备中进行传输,可以把这部分数据保存在共享内存中(此过程中你需要通知客户端半关闭,防止其继续传输,完成继承后再开启)。


上面方法太麻烦了,更简便的方法是,数据分帧,客户端每发送的一个数据帧代表一次请求,当请求数为零以及当前时间是凌晨(低负载时间段)时,断连升级,因为客户端一般会重连,所以没关系。同时你要保证重启尽可能快,我做到了在毫秒内进行重启。

要回复问题请先登录注册