trpc.group/trpc-go/trpc-go@v1.0.3/server/serve_unix.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 //go:build !windows 15 // +build !windows 16 17 package server 18 19 import ( 20 "os" 21 "os/signal" 22 "strconv" 23 "sync" 24 "syscall" 25 "time" 26 27 "github.com/hashicorp/go-multierror" 28 29 "trpc.group/trpc-go/trpc-go/log" 30 "trpc.group/trpc-go/trpc-go/transport" 31 ) 32 33 // DefaultServerCloseSIG are signals that trigger server shutdown. 34 var DefaultServerCloseSIG = []os.Signal{syscall.SIGINT, syscall.SIGTERM, syscall.SIGSEGV} 35 36 // DefaultServerGracefulSIG is signal that triggers server graceful restart. 37 var DefaultServerGracefulSIG = syscall.SIGUSR2 38 39 // Serve implements Service, starting all services that belong to the server. 40 func (s *Server) Serve() error { 41 defer log.Sync() 42 if len(s.services) == 0 { 43 panic("service empty") 44 } 45 s.signalCh = make(chan os.Signal) 46 s.closeCh = make(chan struct{}) 47 48 var ( 49 mu sync.Mutex 50 err error 51 ) 52 for name, service := range s.services { 53 go func(n string, srv Service) { 54 if e := srv.Serve(); e != nil { 55 mu.Lock() 56 err = multierror.Append(err, e).ErrorOrNil() 57 mu.Unlock() 58 s.failedServices.Store(n, srv) 59 time.Sleep(time.Millisecond * 300) 60 s.signalCh <- syscall.SIGTERM 61 } 62 }(name, service) 63 } 64 signal.Notify(s.signalCh, append(DefaultServerCloseSIG, DefaultServerGracefulSIG)...) 65 66 var sig os.Signal 67 select { 68 case <-s.closeCh: 69 case sig = <-s.signalCh: 70 } 71 72 // graceful restart. 73 if sig == DefaultServerGracefulSIG { 74 if _, err := s.StartNewProcess(); err != nil { 75 panic(err) 76 } 77 } 78 // try to close server. 79 s.tryClose() 80 81 if err != nil { 82 log.Errorf(`service serve errors: %+v 83 Note: it is normal to have "use of closed network connection" error during hot restart. 84 DO NOT panic.`, err) 85 } 86 return err 87 } 88 89 // StartNewProcess starts a new process. 90 func (s *Server) StartNewProcess(args ...string) (uintptr, error) { 91 pid := os.Getpid() 92 log.Infof("process: %d, received graceful restart signal, so restart the process", pid) 93 94 // pass tcp listeners' Fds and udp conn's Fds 95 listenersFds := transport.GetListenersFds() 96 97 files := []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()} 98 99 os.Setenv(transport.EnvGraceRestart, "1") 100 os.Setenv(transport.EnvGraceFirstFd, strconv.Itoa(len(files))) 101 os.Setenv(transport.EnvGraceRestartFdNum, strconv.Itoa(len(listenersFds))) 102 os.Setenv(transport.EnvGraceRestartPPID, strconv.Itoa(os.Getpid())) 103 104 files = append(files, prepareListenFds(listenersFds)...) 105 106 execSpec := &syscall.ProcAttr{ 107 Env: os.Environ(), 108 Files: files, 109 } 110 111 os.Args = append(os.Args, args...) 112 childPID, err := syscall.ForkExec(os.Args[0], os.Args, execSpec) 113 if err != nil { 114 log.Errorf("process: %d, failed to forkexec with err: %s", pid, err.Error()) 115 return 0, err 116 } 117 118 return uintptr(childPID), nil 119 } 120 121 func prepareListenFds(fds []*transport.ListenFd) []uintptr { 122 files := make([]uintptr, 0, len(fds)) 123 for _, fd := range fds { 124 files = append(files, fd.Fd) 125 } 126 return files 127 }