Gin微服务框架_golang web框架_完整示例Demo

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 //@BasePath func 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 requests func InitErrorHandler(c *gin.Context) { c.Next() if len(c.Errors) > 0 { c.JSON(http.StatusBadRequest, gin.H{ "errors": c.Errors, }) } }//未知路由处理 返回json func InitNoRouteJson(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{ "code": http.StatusNotFound, "msg":"path not found", }) }//未知调用方式 返回json func 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.Logger var 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 Info 2021/07/23 21:30:36 create docs.go atdocs/docs.go#启动项目 go run main.go #打印如下,表示成功启动8080端口 Listening and serving HTTP on :8080

Gin微服务框架_golang web框架_完整示例Demo
文章图片

浏览器访问接口:
http://127.0.0.1:8080/gin_dem...
{"ping":"pong"}
浏览器访问swagger:
【Gin微服务框架_golang web框架_完整示例Demo】Gin微服务框架_golang web框架_完整示例Demo
文章图片

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...

    推荐阅读