增加多进程模式的示例
This commit is contained in:
parent
84bc7b207b
commit
18033415c2
152
prefork/prefork.go
Normal file
152
prefork/prefork.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c = flag.Int("c", 10, "concurrency level, default 10")
|
||||||
|
prefork = flag.Bool("prefork", false, "use prefork mode")
|
||||||
|
child = flag.Bool("child", false, "is child process")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var ln net.Listener
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if *prefork { // 如果要启动子进程模式
|
||||||
|
ln = doPrefork(*c)
|
||||||
|
} else {
|
||||||
|
ln, err = net.Listen("tcp", ":8972")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start(ln) // 处理 net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(ln net.Listener) {
|
||||||
|
log.Println("started")
|
||||||
|
for {
|
||||||
|
conn, e := ln.Accept()
|
||||||
|
if e != nil {
|
||||||
|
var ne net.Error
|
||||||
|
if errors.As(e, &ne) && ne.Temporary() {
|
||||||
|
log.Printf("accept temp err: %v", ne)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("accept err: %v", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(conn, conn) // 实现 echo 协议,将收到的东西原样返回
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("copy err: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doPrefork(c int) net.Listener {
|
||||||
|
if *child {
|
||||||
|
// 子进程:从文件描述符恢复监听器
|
||||||
|
listener, err := net.FileListener(os.NewFile(3, ""))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优雅关闭处理
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigCh, syscall.SIGTERM)
|
||||||
|
go func() {
|
||||||
|
<-sigCh
|
||||||
|
err := listener.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主进程:创建监听器并将其传递给子进程
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", ":8972")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
tcpListener, err := net.ListenTCP("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fl, err := tcpListener.File()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
// 主进程不需要保持监听
|
||||||
|
if err := tcpListener.Close(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动子进程并持续监控
|
||||||
|
children := make([]*exec.Cmd, c)
|
||||||
|
|
||||||
|
// 处理信号以优雅关闭
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
// 启动子进程管理协程
|
||||||
|
go func() {
|
||||||
|
<-sigCh
|
||||||
|
// 收到信号后通知所有子进程优雅关闭
|
||||||
|
for _, child := range children {
|
||||||
|
if child.Process != nil {
|
||||||
|
if err := child.Process.Signal(syscall.SIGTERM); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 声明子进程启动函数以便递归调用
|
||||||
|
var startChildProcess func(int)
|
||||||
|
startChildProcess = func(i int) {
|
||||||
|
cmd := exec.Command(os.Args[0], "-prefork", "-child")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.ExtraFiles = []*os.File{fl}
|
||||||
|
children[i] = cmd
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
log.Fatalf("启动子进程 %d 失败: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在单独的协程中等待子进程结束并重启
|
||||||
|
go func() {
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
log.Printf("子进程 %d 异常退出: %v,正在重启...", i, err)
|
||||||
|
startChildProcess(i) // 递归重启
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// 启动和监控子进程
|
||||||
|
for i := range children {
|
||||||
|
startChildProcess(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主进程保持运行,但不处理连接
|
||||||
|
select {} // 阻塞主进程
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user