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 }