gitee.com/woood2/luca@v1.0.4/cmd/backend/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 _ "gitee.com/woood2/luca/cmd/backend/docs" 7 "gitee.com/woood2/luca/cmd/backend/internal/assembly" 8 "gitee.com/woood2/luca/internal/cache" 9 "gitee.com/woood2/luca/internal/conf" 10 "gitee.com/woood2/luca/internal/db" 11 "gitee.com/woood2/luca/internal/discovery" 12 "gitee.com/woood2/luca/internal/errcode" 13 myLog "gitee.com/woood2/luca/internal/log" 14 "gitee.com/woood2/luca/internal/producer" 15 "gitee.com/woood2/luca/internal/status" 16 "gitee.com/woood2/luca/internal/trace" 17 "github.com/gin-contrib/cors" 18 "github.com/gin-gonic/gin" 19 "github.com/go-kit/kit/sd" 20 opzipkin "github.com/openzipkin/zipkin-go" 21 swaggerFiles "github.com/swaggo/files" 22 ginSwagger "github.com/swaggo/gin-swagger" 23 "go.uber.org/zap" 24 "log" 25 "net" 26 "net/http" 27 "net/http/httputil" 28 "os" 29 "os/signal" 30 "runtime/debug" 31 "strconv" 32 "strings" 33 "syscall" 34 "time" 35 ) 36 37 const entrance = "backend" 38 39 // @title Luca APIs 40 // @version 1.0 41 // @description 基于Gin封装的demo项目 42 // @securityDefinitions.apikey ApiKeyAuth 43 // @in header 44 // @name token 45 // @BasePath / 46 func main() { 47 //config 48 attr := conf.Load("application.yml", "configs/application.yml") 49 //consul 50 client, consulClient := discovery.Client(attr.Consul.Host, attr.Consul.Port) 51 conf.MergeConsul(attr, consulClient) 52 //zap logger 53 logger := myLog.Build(attr.Env, attr.Project, entrance, attr.Host, attr.ConsoleLog) 54 defer logger.Sync() 55 //gorm & mongo & redis & kafka 56 gormDB := db.NewGormDB(attr.Env, attr.Mysql, logger) 57 db.SetGlobalGormDB(gormDB) 58 mongoDB := db.NewMongoDB(attr.Mongo) 59 db.SetMongoDB(mongoDB) 60 redisCache := cache.NewRedis(attr.Redis) 61 cache.SetRedis(redisCache) 62 p := producer.New(attr.Kafka, logger) 63 defer p.Close() 64 producer.SetG(p) 65 //zipkin 66 trace.Open(attr.Zipkin) 67 defer trace.Close() 68 //sdk 69 assembly.SetupSDK(client, logger) 70 //register 71 var registar sd.Registrar 72 if attr.Backend.Register { 73 ho := strings.Split(attr.Backend.Addr, ":") 74 if port, err := strconv.Atoi(ho[1]); err != nil { 75 log.Panicf("attr.Backend.Addr error: %s\n", err) 76 } else { 77 registar = discovery.RegisterRestful(client, attr.Project+"_"+entrance, attr.Host, port) 78 registar.Register() 79 } 80 } 81 //gin 82 if conf.IsPro(attr.Env) { 83 gin.SetMode(gin.ReleaseMode) 84 } 85 r := gin.New() 86 r.Use(loggerWithZap(logger, time.RFC3339, false)) 87 r.Use(recoveryWithZap(logger, true, attr.Env)) 88 config := cors.DefaultConfig() 89 config.AllowOrigins = attr.Backend.AllowOrigins 90 r.Use(cors.New(config)) 91 //Pprof & prometheus & hystrix 92 go status.Pprof(attr.Pprof, attr.Env, attr.Backend.PprofAddr) 93 go status.Prometheus(attr.Backend.MetricsAddr) 94 go status.Hystrix(attr.Backend.HystrixPort) 95 //swagger 96 if !conf.IsPro(attr.Env) { 97 url := ginSwagger.URL("http://" + attr.Host + attr.Backend.Addr + "/swagger/doc.json") 98 r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url)) 99 } 100 //handler 101 assembly.RegisterHandler(attr.Project, entrance, attr.Env, r, logger, gormDB) 102 //run 103 srv := &http.Server{ 104 Addr: attr.Backend.Addr, 105 Handler: r, 106 } 107 go func() { 108 if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { 109 log.Panicf("listen: %s\n", err) 110 } 111 }() 112 //graceful shutdown 113 quit := make(chan os.Signal) 114 signal.Notify(quit, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2) 115 <-quit 116 log.Println("Shutting down server...") 117 if attr.Backend.Register { 118 registar.Deregister() //gray release 119 time.Sleep(1 * time.Second) //make sure 120 } 121 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 122 defer cancel() 123 if err := srv.Shutdown(ctx); err != nil { 124 log.Panicln("Server forced to shutdown:", err) 125 } 126 log.Println("Server exiting") 127 } 128 129 func loggerWithZap(logger *zap.Logger, timeFormat string, utc bool) gin.HandlerFunc { 130 return func(c *gin.Context) { 131 start := time.Now() 132 // some evil middlewares modify this values 133 path := c.Request.URL.Path 134 query := c.Request.URL.RawQuery 135 c.Next() 136 137 end := time.Now() 138 latency := end.Sub(start) 139 if utc { 140 end = end.UTC() 141 } 142 143 if len(c.Errors) > 0 { 144 // Append error field if this is an erroneous request. 145 for _, e := range c.Errors.Errors() { 146 logger.Error(e) 147 } 148 } else { 149 traceID := "-" 150 if s, ok := c.Keys[trace.SpanKey()]; ok { 151 span := s.(opzipkin.Span) 152 traceID = span.Context().TraceID.String() 153 } 154 155 logger.Info(path, 156 zap.Int("status", c.Writer.Status()), 157 zap.String("method", c.Request.Method), 158 zap.String("path", path), 159 zap.String("traceID", traceID), 160 zap.String("query", query), 161 zap.String("ip", c.ClientIP()), 162 zap.String("user-agent", c.Request.UserAgent()), 163 zap.String("time", end.Format(timeFormat)), 164 zap.Duration("latency", latency), 165 ) 166 } 167 } 168 } 169 170 func recoveryWithZap(logger *zap.Logger, stack bool, env string) gin.HandlerFunc { 171 return func(c *gin.Context) { 172 defer func() { 173 if err := recover(); err != nil { 174 // Check for a broken connection, as it is not really a 175 // condition that warrants a panic stack trace. 176 var brokenPipe bool 177 if ne, ok := err.(*net.OpError); ok { 178 if se, ok := ne.Err.(*os.SyscallError); ok { 179 if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { 180 brokenPipe = true 181 } 182 } 183 } 184 185 httpRequest, _ := httputil.DumpRequest(c.Request, false) 186 if brokenPipe { 187 logger.Error(c.Request.URL.Path, 188 zap.Any("error", err), 189 zap.String("request", string(httpRequest)), 190 ) 191 // If the connection is dead, we can't write a status to it. 192 c.Error(err.(error)) // nolint: errcheck 193 c.Abort() 194 return 195 } 196 197 traceID := "-" 198 if s, ok := c.Keys[trace.SpanKey()]; ok { 199 span := s.(opzipkin.Span) 200 traceID = span.Context().TraceID.String() 201 } 202 203 detail := string(debug.Stack()) 204 if stack { 205 logger.Error("[Recovery from panic]", 206 zap.Time("time", time.Now()), 207 zap.Any("error", err), 208 zap.String("traceID", traceID), 209 zap.String("request", string(httpRequest)), 210 zap.String("stack", detail), 211 ) 212 } else { 213 logger.Error("[Recovery from panic]", 214 zap.Time("time", time.Now()), 215 zap.Any("error", err), 216 zap.String("traceID", traceID), 217 zap.String("request", string(httpRequest)), 218 ) 219 } 220 resp := &errcode.Resp{ 221 ErrCode: errcode.RestfulErrCode[errcode.ServerErr], 222 ErrMsg: errcode.ServerErr.Error(), 223 Data: nil, 224 TraceID: traceID, 225 } 226 if !conf.IsPro(env) && stack { 227 resp.Stack = fmt.Sprintf("Recovery from panic: %+v", err) 228 resp.Stack += "\n" + detail 229 } 230 c.JSON(500, resp) 231 c.Abort() 232 } 233 }() 234 c.Next() 235 } 236 }