Gin简介

Gin是一个golang的微框架,封装比拟优雅,API敌对,源码正文比拟明确,具备疾速灵便,容错不便等特点。其实对于golang而言,web框架的依赖要远比Python,Java之类的要小。本身的net/http足够简略,性能也十分不错。框架更像是一些罕用函数或者工具的汇合。借助框架开发,不仅能够省去很多罕用的封装带来的工夫,也有助于团队的编码格调和造成标准。

gin特点
  • 性能优良
  • 基于官网的net/http的无限封装
  • 不便 灵便的中间件
  • 数据绑定很弱小
  • 社区比拟沉闷

官网源代码地址: https://github.com/gin-gonic/gin

gin web微服务框架示例

总体性能

  • 集成logrus.Logger日志按天切割,json格局打印
  • 集成swagger文档
  • 指定yml配置文件启动
  • 异样解决
  • 拦截器打印申请和响应参数
main.go我的项目入口

init办法: 初始化相干配置
main办法: 下面的正文定义了swagger信息,而后gin初始化,路由初始化,是否启用swagger

package mainimport (    "flag"    "fmt"    . "gin_demo/config"    _ "gin_demo/docs"    . "gin_demo/log"    "gin_demo/router"    "github.com/gin-gonic/gin"    "github.com/swaggo/gin-swagger"    "github.com/swaggo/gin-swagger/swaggerFiles"    "runtime"    "time")var version = flag.Bool("version", true, "是否打印版本,默认打印")var swagger = flag.Bool("swagger", true, "是否启动swagger接口文档,默认不启动")var configFile = flag.String("configFile", "config/config.yml", "配置文件门路")var projectPath = flag.String("projectPath", "/gin_demo", "我的项目拜访门路前缀")func init(){    flag.Parse()    ConfigRead(*configFile)    LogInit()}//@title gin示例 API//@version 0.0.1//@description  相干接口文档//@host 127.0.0.1:8080//@BasePathfunc main() {    if *version {        showVersion := fmt.Sprintf("%s %s@%s", "gin_demo", "1.0.0", time.Now().Format("2006-01-02 15:04:05"))        fmt.Println(showVersion)        fmt.Println("go version: " + runtime.Version())    }    Log.Info("start server...")    gin.SetMode(gin.DebugMode) //全局设置环境,此为开发环境,线上环境为gin.ReleaseMode    router.GinInit()    //gin工程实例 *gin.Engine    r := router.Router    //路由初始化    router.SetupRouter(*projectPath)    if *swagger {        //启动拜访swagger文档        r.GET(*projectPath + "/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))    }    Log.Info("listen on :%s", Cfg.ListenPort)    //监听端口    r.Run(":" + Cfg.ListenPort)}
router.go 路由
package routerimport (    "github.com/gin-gonic/gin"    "net/http")var Router *gin.Enginefunc GinInit()  {    // 禁用控制台色彩    //gin.DisableConsoleColor()    //gin.New()返回一个*Engine 指针    //而gin.Default()岂但返回一个*Engine 指针,而且还进行了debugPrintWARNINGDefault()和engine.Use(Logger(), Recovery())其余的一些中间件操作    Router = gin.Default()    //Router = gin.New()}func SetupRouter(projectPath string) {    //应用日志    //Router.Use(gin.Logger())    //应用Panic解决计划    //Router.Use(gin.Recovery())    Router.Use(InitErrorHandler)    Router.Use(InitAccessLogMiddleware)    // 未知调用形式    Router.NoMethod(InitNoMethodJson)    // 未知路由解决    Router.NoRoute(InitNoRouteJson)    // Ping    Router.GET(projectPath + "/ping", func(c *gin.Context) {        c.JSON(http.StatusOK, gin.H{            "ping": "pong",        })    })    Router.POST(projectPath + "/pp", func(c *gin.Context) {        c.JSON(http.StatusOK, gin.H{            "ping": "post",        })    })}
middleware.go 中间件拦截器
package routerimport (    "encoding/json"    . "gin_demo/log"    . "gin_demo/threadlocal"    "github.com/gin-gonic/gin"    . "github.com/jtolds/gls"    "github.com/sirupsen/logrus"    "io/ioutil"    "net/http"    "strconv"    "time")// ErrorHandler is a middleware to handle errors encountered during requestsfunc InitErrorHandler(c *gin.Context) {    c.Next()    if len(c.Errors) > 0 {        c.JSON(http.StatusBadRequest, gin.H{            "errors": c.Errors,        })    }}//未知路由解决 返回jsonfunc InitNoRouteJson(c *gin.Context) {    c.JSON(http.StatusNotFound, gin.H{        "code": http.StatusNotFound,        "msg":  "path not found",    })}//未知调用形式 返回jsonfunc InitNoMethodJson(c *gin.Context) {    c.JSON(http.StatusMethodNotAllowed, gin.H{        "code": http.StatusMethodNotAllowed,        "msg":  "method not allowed",    })}//打印申请和响应日志func InitAccessLogMiddleware(c *gin.Context) {    //request id    requestId := c.Request.Header.Get("X-RequestId")    if requestId == "" {        requestId = strconv.FormatInt(time.Now().UnixNano(), 10)    }    //response requestId    c.Writer.Header().Set("X-RequestId", requestId)    // 开始工夫    startTime := time.Now()    //解决申请 do chian    Mgr.SetValues(Values{Rid: requestId}, func() {        c.Next()    })    // 完结工夫    endTime := time.Now()    // 执行工夫    latencyTime := endTime.Sub(startTime)    // 申请形式    reqMethod := c.Request.Method    // 申请路由    reqUri := c.Request.RequestURI    // 状态码    statusCode := c.Writer.Status()    // 申请IP    clientIP := c.ClientIP()    //申请参数    body, _ := ioutil.ReadAll(c.Request.Body)    //返回参数    responseMap := c.Keys    responseJson, _ := json.Marshal(responseMap)    //日志格局    //LogAccess.Infof("| %3d | %13v | %15s | %s | %s | %s | %s | %s |",    //    statusCode,    //    latencyTime,    //    clientIP,    //    reqMethod,    //    reqUri,    //    requestId,    //    string(body),    //    string(responseJson),    //)    // 日志格局    LogAccess.WithFields(logrus.Fields{        "status_code":  statusCode,        "latency_time": latencyTime,        "client_ip":    clientIP,        "req_method":   reqMethod,        "req_uri":      reqUri,        "req_Id":       requestId,        "req_body":     string(body),        "res_body":     string(responseJson),    }).Info()}
logger.go 日志定义和配置
package logimport (    "fmt"    "gin_demo/config"    "github.com/sirupsen/logrus"    rotatelogs "github.com/lestrrat-go/file-rotatelogs"    "github.com/rifflock/lfshook"    "os"    "path"    "time")var Log *logrus.Loggervar LogAccess *logrus.Loggerfunc LogInit() {    logFilePath := ""    logPath := config.Cfg.LogPath    if len(logPath) == 0 {        //获取当前目录        if dir, err := os.Getwd(); err == nil {            logFilePath = dir + "/logs/"        }    } else {        //指定目录        logFilePath = logPath + "/logs/"    }    if err := os.MkdirAll(logFilePath, 0777); err != nil {        fmt.Println(err.Error())    }    rootLogInit(logFilePath)    accessLogInit(logFilePath)}func rootLogInit(logFilePath string) {    logFileName := "root.log"    //日志文件    fileName := path.Join(logFilePath, logFileName)    if _, err := os.Stat(fileName); err != nil {        if _, err := os.Create(fileName); err != nil {            fmt.Println(err.Error())        }    }    //写入文件    src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)    if err != nil {        fmt.Println("err", err)    }    //实例化    Log = logrus.New()    //设置输入    Log.Out = src    Log.Out = os.Stdout    //设置日志级别    Log.SetLevel(logrus.DebugLevel)    // 设置 rotatelogs    logWriter, err := rotatelogs.New(        // 宰割后的文件名称        fileName + "-%Y%m%d.log",        // 生成软链,指向最新日志文件        rotatelogs.WithLinkName(fileName),        // 设置最大保留工夫(2天)        rotatelogs.WithMaxAge(2*24*time.Hour),        // 设置日志切割工夫距离(1天)        rotatelogs.WithRotationTime(24*time.Hour),    )    writeMap := lfshook.WriterMap{        logrus.InfoLevel:  logWriter,        logrus.FatalLevel: logWriter,        logrus.DebugLevel: logWriter,        logrus.WarnLevel:  logWriter,        logrus.ErrorLevel: logWriter,        logrus.PanicLevel: logWriter,    }    //设置日志格局    lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{        TimestampFormat:"2006-01-02 15:04:05",    })    // 新增 Hook    Log.AddHook(lfHook)}func accessLogInit(logFilePath string) {    logFileNameAccess := "access.log"    fileNameAccess := path.Join(logFilePath, logFileNameAccess)    if _, err := os.Stat(fileNameAccess); err != nil {        if _, err := os.Create(fileNameAccess); err != nil {            fmt.Println(err.Error())        }    }    srcAccess, err := os.OpenFile(fileNameAccess, os.O_APPEND|os.O_WRONLY, os.ModeAppend)    if err != nil {        fmt.Println("err", err)    }    //实例化    LogAccess = logrus.New()    //设置输入    LogAccess.Out = srcAccess    LogAccess.Out = os.Stdout    //设置日志级别    LogAccess.SetLevel(logrus.DebugLevel)    // 设置 rotatelogs    logWriterAccess, err := rotatelogs.New(        // 宰割后的文件名称        fileNameAccess + "-%Y%m%d.log",        // 生成软链,指向最新日志文件        rotatelogs.WithLinkName(fileNameAccess),        // 设置最大保留工夫(2天)        rotatelogs.WithMaxAge(2*24*time.Hour),        // 设置日志切割工夫距离(1天)        rotatelogs.WithRotationTime(24*time.Hour),    )    writeMapAccess := lfshook.WriterMap{        logrus.InfoLevel:  logWriterAccess,        logrus.FatalLevel: logWriterAccess,        logrus.DebugLevel: logWriterAccess,        logrus.WarnLevel:  logWriterAccess,        logrus.ErrorLevel: logWriterAccess,        logrus.PanicLevel: logWriterAccess,    }    lfHookAccess := lfshook.NewHook(writeMapAccess, &logrus.JSONFormatter{        TimestampFormat:"2006-01-02 15:04:05",    })    // 新增 Hook    LogAccess.AddHook(lfHookAccess)}
Demo运行

swag 的装置应用后续会解说

#执行:swag init 生成swagger文件gin_demo git:(main) swag init#显示如下,会在我的项目生成docs文件夹2021/07/23 21:30:36 Generate swagger docs....2021/07/23 21:30:36 Generate general API Info2021/07/23 21:30:36 create docs.go at  docs/docs.go#启动我的项目go run main.go #打印如下,示意胜利启动8080端口Listening and serving HTTP on :8080

浏览器拜访接口:
http://127.0.0.1:8080/gin_dem...

{"ping":"pong"}

浏览器拜访swagger:

Demo源代码地址:https://github.com/tw-iot/gin...

参考链接地址:
http://www.topgoer.com/gin%E6...
https://zhuanlan.zhihu.com/p/...
https://github.com/skyhee/gin...
https://www.jianshu.com/p/989...