github.com/kaydxh/golang@v0.0.131/pkg/webserver/webserver.go (about)

     1  /*
     2   *Copyright (c) 2022, kaydxh
     3   *
     4   *Permission is hereby granted, free of charge, to any person obtaining a copy
     5   *of this software and associated documentation files (the "Software"), to deal
     6   *in the Software without restriction, including without limitation the rights
     7   *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   *copies of the Software, and to permit persons to whom the Software is
     9   *furnished to do so, subject to the following conditions:
    10   *
    11   *The above copyright notice and this permission notice shall be included in all
    12   *copies or substantial portions of the Software.
    13   *
    14   *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   *SOFTWARE.
    21   */
    22  package webserver
    23  
    24  import (
    25  	"context"
    26  	"sync"
    27  	"time"
    28  
    29  	"github.com/gin-gonic/gin"
    30  	context_ "github.com/kaydxh/golang/go/context"
    31  	syscall_ "github.com/kaydxh/golang/go/syscall"
    32  	"github.com/kaydxh/golang/pkg/discovery/consul"
    33  	gw_ "github.com/kaydxh/golang/pkg/grpc-gateway"
    34  	"github.com/sirupsen/logrus"
    35  )
    36  
    37  type WebHandler interface {
    38  	SetRoutes(ginRouter gin.IRouter, grpcRouter *gw_.GRPCGateway)
    39  }
    40  
    41  type GenericWebServer struct {
    42  	// Server Register. The backend is started after the server starts listening.
    43  	ServiceRegistryBackend *consul.ServiceRegistryServer
    44  
    45  	ginBackend  *gin.Engine
    46  	grpcBackend *gw_.GRPCGateway
    47  
    48  	// PostStartHooks are each called after the server has started listening, in a separate go func for each
    49  	// with no guarantee of ordering between them.  The map key is a name used for error reporting.
    50  	// It may kill the process with a panic if it wishes to by returning an error.
    51  	postStartHookLock    sync.Mutex
    52  	postStartHooks       map[string]postStartHookEntry
    53  	postStartHooksCalled bool
    54  
    55  	preShutdownHookLock    sync.Mutex
    56  	preShutdownHooks       map[string]preShutdownHookEntry
    57  	preShutdownHooksCalled bool
    58  
    59  	// healthz checks
    60  	//	healthzLock            sync.Mutex
    61  	//	healthzChecks          []healthz.HealthCheck
    62  	//	healthzChecksInstalled bool
    63  	// livez checks
    64  	//	livezLock            sync.Mutex
    65  	//	livezChecks          []healthz.HealthCheck
    66  	//	livezChecksInstalled bool
    67  	// readyz checks
    68  	//	readyzLock            sync.Mutex
    69  	//	readyzChecks          []healthz.HealthCheck
    70  	//	readyzChecksInstalled bool
    71  	//	livezGracePeriod      time.Duration
    72  
    73  	// the readiness stop channel is used to signal that the apiserver has initiated a shutdown sequence, this
    74  	// will cause readyz to return unhealthy.
    75  	readinessStopCh chan struct{}
    76  
    77  	// ShutdownDelayDuration allows to block shutdown for some time, e.g. until endpoints pointing to this API server
    78  	// have converged on all node. During this time, the API server keeps serving, /healthz will return 200,
    79  	// but /readyz will return failure.
    80  	ShutdownDelayDuration time.Duration
    81  
    82  	ShutdownTimeoutDuration time.Duration
    83  
    84  	// The limit on the request body size that would be accepted and decoded in a write request.
    85  	// 0 means no limit.
    86  	maxRequestBodyBytes int64
    87  
    88  	// WebServerID is the ID of this Web server
    89  	WebServerID string
    90  }
    91  
    92  // preparedGenericWebServer is a private wrapper that enforces a call of PrepareRun() before Run can be invoked.
    93  type preparedGenericWebServer struct {
    94  	*GenericWebServer
    95  }
    96  
    97  func (s preparedGenericWebServer) NonBlockingRun(ctx context.Context) (context.Context, error) {
    98  
    99  	ctx, cancel := context.WithCancel(ctx)
   100  	go func() {
   101  		defer cancel()
   102  		err := s.grpcBackend.ListenAndServe()
   103  		if err != nil {
   104  			return
   105  		}
   106  	}()
   107  
   108  	return ctx, nil
   109  }
   110  
   111  func (s preparedGenericWebServer) Run(ctx context.Context) error {
   112  	ctx, cancel := context.WithCancel(ctx)
   113  	defer cancel()
   114  
   115  	logrus.Infof("Installing http server on %s", s.grpcBackend.Addr)
   116  	ctx, err := s.NonBlockingRun(ctx)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	s.RunPostStartHooks(ctx)
   121  	logrus.Infof("Installed http server on %s", s.grpcBackend.Addr)
   122  
   123  	<-ctx.Done()
   124  	// run shutdown hooks directly. This includes deregistering from the kubernetes endpoint in case of kube-apiserver.
   125  	err = s.RunPreShutdownHooks()
   126  	if err != nil {
   127  		logrus.Errorf("failed to run pre shutted down hook, err: %v", err)
   128  		return err
   129  	}
   130  
   131  	//shutdown
   132  	shutDownCtx, shutDownCancel := context_.WithTimeout(context.Background(), s.ShutdownTimeoutDuration)
   133  	defer shutDownCancel()
   134  
   135  	err = s.grpcBackend.Shutdown(shutDownCtx)
   136  	if err != nil {
   137  		logrus.Errorf("failed to shutted down http server on %s, err: %v", s.grpcBackend.Addr, err)
   138  		return err
   139  	}
   140  	logrus.Infof("Shutted down http server on %s", s.grpcBackend.Addr)
   141  	return nil
   142  }
   143  
   144  func (s *GenericWebServer) PrepareRun() (preparedGenericWebServer, error) {
   145  	if s.grpcBackend != nil {
   146  		// assgined ginBackend to grpc handler
   147  		s.grpcBackend.Handler = s.ginBackend
   148  	}
   149  
   150  	curOpenFiles, err := syscall_.SetMaxNumFiles()
   151  	if err != nil {
   152  		logrus.Errorf("failed to set max num files, err: %v", err)
   153  		return preparedGenericWebServer{}, err
   154  	}
   155  	logrus.Infof("set %v num files", curOpenFiles)
   156  	return preparedGenericWebServer{s}, nil
   157  }
   158  
   159  func (s *GenericWebServer) InstallWebHandlers(handlers ...WebHandler) {
   160  	for _, h := range handlers {
   161  		if h == nil {
   162  			continue
   163  		}
   164  		h.SetRoutes(s.ginBackend, s.grpcBackend)
   165  	}
   166  }