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

2023-10-29

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 main

import (
	"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 router

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

var Router *gin.Engine

func 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 router

import (
	"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 log

import (
	"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.Logger

func 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 at  docs/docs.go

#启动项目
go run main.go 
#打印如下,表示成功启动8080端口
Listening and serving HTTP on :8080

在这里插入图片描述

浏览器访问接口:
http://127.0.0.1:8080/gin_demo/ping

{“ping”:“pong”}

浏览器访问swagger:

在这里插入图片描述

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

参考链接地址:
http://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/%E7%AE%80%E4%BB%8B.html
https://zhuanlan.zhihu.com/p/165633941
https://github.com/skyhee/gin-doc-cn
https://www.jianshu.com/p/98965b3ff638

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Gin微服务框架_golang web框架_完整示例Demo 的相关文章

  • Go的并发的退出

    有时候我们需要通知goroutine停止它正在干的事情 比如一个正在执行计算的web服务 然而它的客户端已经断开了和服务端的连接 Go语言并没有提供在一个goroutine中终止另一个goroutine的方法 由于这样会导致goroutin
  • go踩坑——no required module provides package go.mod file not found in current directory or any parent

    背景 准备运行下面代码 package main import github com gin gonic gin func main 创建一个默认的路由引擎 r gin Default GET 请求方式 hello 请求的路径 当客户端以G
  • golang基础教程

    目录 golang基础教程 一 环境搭建 golang基础教程 二 开发规范及API golang基础教程 三 变量与数据类型概述 golang基础教程 四 基本数据类型 golang基础教程 五 基本数据类型的转换 golang基础教程
  • Golang-使用 goroutine 运行闭包的“坑”

    介绍 在 Go 语言中 函数支持匿名函数 闭包就是一种特殊的匿名函数 它可以用于访问函数体外部的变量 需要注意的是 在 for range 中 使用 goroutine 执行闭包时 经常会掉 坑 因为匿名函数可以访问函数体外部的变量 而 f
  • golang sleep

    golang的休眠可以使用time包中的sleep 函数原型为 func Sleep d Duration 其中的Duration定义为 type Duration int64 Duration的单位为 nanosecond 为了便于使用
  • Golang连接Jenkins获取Job Build状态及相关信息

    文章目录 1 连接Jenkins 2 controller 3 module 4 router 5 效果展示 第三方包 gojenkins 方法文档 gojenkins docs 实现起来很简单 利用第三方库 连接jenkins 调用相关方
  • beego+goAdmin+mysql+docker+natapp作为微信小程序地服务器“伪部署”

    写在前面的话 1 为什么我要叫伪部署 答 因为我把它们放在服务器运行 都是开发模式 生产模式实在不会弄 所以就这样了 2 系统环境 答 腾讯云服务器 系统为 ubuntu 版本不记得 应该是比较高的 3 前提假设 答 假设你的服务器已经安装
  • Go语言里面的各种疑难杂症

    什么是闭包 闭包有什么缺陷 func AddUpper func int int var n int 10 return func x int int n n x return n func main f AddUpper fmt Prin
  • goland环境配置

    goland modules环境配置 下载和安装goland 环境配置 配置环境变量GOPATH 配置go modules GOPROXY代理的系统变量 工程目录中新建三个工作目录 goland中启用go modules 新建一个go程序
  • 掌握 Go 语言中的循环结构:从基础到高级

    一 if else 分支结构 1 if 条件判断基本写法 package main import fmt func main score 65 if score gt 90 fmt Println A else if score gt 75
  • 有哪些不错的 Golang 开源项目?

    目前人在字节做 Go 开发 寻找 Golang 开源项目学习目的可能是 想学习或者提高自己对 Go 项目的组织和编排能力 想学习 Go 项目的框架设计 想在一些 Go 语法上细节的优化和进阶 我推荐两个项目 一 tinode 这是一个开源的
  • 【go语言开发】编写单元测试

    本文主要介绍使用go语言编写单元测试用例 首先介绍如何编写单元测试 然后介绍基本命令的使用 最后给出demo示例 文章目录 前言 命令 示例 前言 在go语言中编写单元测试时 使用说明 测试文件命名 在 Go 语言中 测试文件的命名应与被测
  • go-zero开发入门之网关往rpc服务传递数据2

    go zero 的网关服务实际是个 go zero 的 API 服务 也就是一个 http 服务 或者说 rest 服务 http 转 grpc 使用了开源的 grpcurl 库 当网关需要往 rpc 服务传递额外的数据 比如鉴权数据的时候
  • go-zero开发入门之网关往rpc服务传递数据1

    go zero 的网关往 rpc 服务传递数据时 可以使用 headers 但需要注意前缀规则 否则会发现数据传递不过去 或者对方取不到数据 go zero 的网关对服务的调用使用了第三方库 grpcurl 入口函数为 InvokeRPC
  • go-zero 的 etcd 配置

    实现代码在 core discov config go 文件中 type EtcdConf struct Hosts string Key string ID int64 json optional User string json opt
  • “go mod tidy”之错误“not a valid zip file”

    执行 go mod tidy 时 遇到如下错误 rpc imports github com zeromicro go zero zrpc imports github com zeromicro go zero zrpc resolver
  • 【go语言】error错误机制及自定义错误返回类型

    简介 Go 语言通过内置的 error 接口来处理错误 该接口定义如下 type error interface Error string 这意味着任何实现了 Error 方法的类型都可以作为错误类型 在 Go 中 通常使用 errors
  • go开发--操作mysql数据库

    在 Go 中访问 MySQL 数据库并进行读写操作通常需要使用第三方的 MySQL 驱动 Go 中常用的 MySQL 驱动有 github com go sql driver mysql 和 github com go xorm xorm
  • Golang拼接字符串性能对比

    g o l a n g golang g o l an g
  • 这套Go语言开发框架组合真的非常高效

    我尝试过很多框架 从Django Flask和Laravel到NextJS和SvelteKit 到目前为止 这是我唯一可以使用的不会让我感到疯狂或者放弃项目的堆栈 框架 我喜欢所有这些框架 但我只是不太适应它们的设计方式 实际上 我是一个弱

随机推荐

  • 闲置资源优化,轻松检查集群中的空闲成本

    前言 Kubernetes 提供了对计算 网络 存储资源的抽象 提升了集群资源管理的效率 然而 由于用户不需要直接管理底层资源 可能导致部分闲置资源未及时发现 造成成本浪费 在企业 IT 成本治理过程中 如何发现并处理这部分资源 是成本优化
  • Nvidia Deepstream极致细节:3. Deepstream Python RTSP视频输出显示

    Nvidia Deepstream极致细节 3 Deepstream Python RTSP视频输出显示 此章节将详细对官方案例 deepstream test 1 rtsp out py作解读 deepstream test 1 rtsp
  • Buuctf——[RCTF2015]EasySQL

    Buuctf RCTF2015 EasySQL 一 解题步骤 1 看到注册登录 闲着没事先注册个号试试 1 123 2 进去看了 除了受到文化熏陶 别的好像没有啥 点一下试试其有什么功能 一不小心就看到了修改密码 3 惊奇的发现 密码可以被
  • 【数据结构】二、顺序表的定义和基本操作的实现

    目录 数据结构 DATA STRUCTURE 二 线性表 2 1 线性表的定义和基本操作概述 2 2 线性表的顺序表示 2 2 1 顺序表存储结构描述和特点 1 静态存储方式 2 动态存储方式 3 顺序表的优缺点 2 2 2 顺序表基本操作
  • 数据库-ER建模

    目录 基本概念 1 实体 entity 2 属性 attribute 3 联系 relation 3 1 基数约束的四种形态 3 1 1 基数约束形态一 3 1 2 基数约束形态二 3 1 3 基数约束形态三 3 1 4 基数约束形态四 扩
  • OneOS文字转语言组件实操

    本文分享自中移OneOS微信公众号 当万耦遇上chaoTTS 作者 柏灵 大家好 柏灵又又又来啦 相信大家已经开始在寒冷的冬季种植鲜嫩的豌豆尖 火锅里烫上豌豆尖 从此打工人有了豌豆尖自由 还没有种植的小伙伴 可以通过OneOS 基于端云融合
  • fit、transform、fit_transform的区别,为什么训练集用 fit_transform , 测试集用 transform

    三个函数的使用具体解释 在数据预处理中的应用 1 只涉及一组数据 fit data 对数据进行拟合 获得了数据的均值 最大最小 标准差等属性值 transform data 利用 fit data 获取到的属性对数据做预处理 如标准化 归一
  • cocos cretor shader effect-the book of shader前言

    前言 说来惭愧 半年前开始接触cocso creator shader的时候 看了官方教程 看了大神写的例子 搞懂了一些语法概念 可是当时看一遍有种似懂非懂的感觉 那种感觉就是听过很多道理 任然过不好一生 在cocos论坛里有大神也分享过自
  • Foxmail 搜索功能找不到全部邮件问题的解决方法。

    经常使用Foxmail办公收发邮件 发现Foxmail 搜索功能找不到全部邮件 原来问题出在搜索上 举例说明 迁移到Foxmail后 王卡卡同志给我发过3封邮件 可是我用据称无比强大的Foxmail搜索邮件时 输入关键字 root 只能找到
  • [计算机毕业设计]数字水印算法

    前言 大四是整个大学期间最忙碌的时光 一边要忙着准备考研 考公 考教资或者实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校要求的毕设项目越来越难 有不少课题是研究生级别难度的 对本科同学来说是充满挑战 为帮助大
  • 梦幻模拟战pc版更新服务器正在维护,梦幻模拟战PC版免CD(更新2代跳出补丁 一些实用说明)...

    最近看到游侠有发梦幻模拟战PC版1 2 下载 不过要禁用DX 还要拔光驱 比较麻烦 我把我以前收藏的L1 L2 免CD发出来分享 我自己反正测试可以用 1代 第1步 替换langpc exe 请做好备份 第2步 复制光盘上 DISK1 ID
  • shell脚本中$!、$@、$#、$$、$0、$1、$2、$*的含义

    一 shell脚本中 0 1 2 的含义 Shell最后运行的后台Process的PID 后台运行的最后一个进程的进程ID号 添加到shell当中参数的个数 Shell本身的PID ProcessID 即脚本运行的当前进程ID号 0 脚本本
  • 【第八章 线程的同步机制(同步代码块、同步方法)】

    第八章 线程的同步机制 同步代码块 同步方法 1 线程的同步机制方式一 同步代码块 java中通过同步机制解决线程安全问题 synchronized 同步监视器 需要被同步的代码 说明 操作共享数据的代码即为需要被同步的代码 共享数据 多个
  • 关于浏览器静止音频自动播放的问题

    背景 今天在制作前端页面时 想要给网页加上会自动播放的音乐 但是用audio标签设置音频的自动播放后 即使将autoplay属性设置成true 谷歌等浏览器页面加载完成后也不会自动播放音乐 尝试了各种办法无果 原因 目前 最为流行的浏览器共
  • 求助TCanvas内存无限涨的问题

    求助TCanvas内存无限涨的问题 Delphi Windows SDK API http www delphi2007 net DelphiMultimedia html delphi 20061110220830160 html pro
  • 利用Wireshark分析TCP三次握手

    首先打开 http www baidu com这个网址进行抓包 首先在过滤器中输入 http过滤 找到 GET HTTP 1 1 我们可以看到在出现了三条TCP记录之后才出现了HTTP这也更加相信HTTP是基于TCP协议的 第一次TCP握手
  • uniapp 顶部头部样式

  • 【Python】剑指offer 14:剪绳子

    题目 给你一根长度为n的绳子 请把绳子剪成m段 m和n都是整数 n gt 1并且m gt 1 每段绳子的长度记为k 0 k 1 k m 请问k 0 k 1 k m 可能的最大乘积是多少 例如 当绳子的长度为8时 我们把它剪成长度分别为2 3
  • idea中类存在编译器报错类无法找到,打包正常解决

    步骤 关键在于清掉类在idea的cache 1 刷新maven项目 2 清理idea缓存 3 maven clean install 4 重新bulid 5 如果使用了lombok插件开启之后重新build 6 maven依赖冲突导致
  • Gin微服务框架_golang web框架_完整示例Demo

    Gin简介 前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住分享一下给大家 点击跳转到网站 Gin是一个golang的微框架 封装比较优雅 API友好 源码注释比较明确 具有快速灵活 容错方便等特点 其实对于golang而