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  }