Gin 学习笔记

教程地址:https://www.bilibili.com/video/BV1FV4y1C72M?spm_id_from=333.788.videopod.sections&vd_source=707ec8983cc32e6e065d5496a7f79ee6


01-项目搭建

  • 各常用目录的说明:
    https://github.com/golang-standards/project-layout/blob/master/README_zh.md

02-优雅启停

  • 用gin启动web服务器
package mainimport ("context""github.com/gin-gonic/gin""log""net/http""os""os/signal""syscall""time"
)func main() {r := gin.Default()srv := &http.Server{Addr:    ":5000",Handler: r,}// 通过协程启动服务go func() {log.Printf("server listen at %s", srv.Addr)if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("listen: %s\n", err)}}()// 制作按Ctrl+C退出功能,此处阻塞quit := make(chan os.Signal)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quit// 系统停止开始log.Println("Shutdown Server ...")// 等待2秒ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()// 停止服务if err := srv.Shutdown(ctx); err != nil {log.Fatal("Server Shutdown:", err)}// 2秒后打印select {case <-ctx.Done():log.Println("timeout of 2 seconds.")}log.Println("Server exiting")
}

03-路由

在这里插入图片描述

  • 路由通过InitRouter初始化;路由与api操作分离开来;api的路由设置与路由执行函数分开
  • router/router.go :路由启动,并挂载user模块的路由
package routerimport ("Gin_gPRC/api/user""github.com/gin-gonic/gin"
)// Router 接口,规定里面有一个Route函数
type Router interface {Route(r *gin.Engine)
}// 定义一个RegisterRouter 注册类,这个类有一个Route方法
// Route方法接收一个符合Router接口规范的对象
// Route方法里运行了接收对象里的Route方法,用以绑定路由
type RegisterRouter struct{}
func New() *RegisterRouter {return &RegisterRouter{}
}
func (*RegisterRouter) Route(router Router, r *gin.Engine) {router.Route(r)
}// 初始化路由
func InitRouter(r *gin.Engine) {router := New()// 把user.RouterUser对象给到注册类,委托运行了user里的Route方法,传递r参数router.Route(&user.RouterUser{}, r)
}
  • api/user/route.go;注册了/login/getCaptcha的POST接口,执行函数写再user.go中
package userimport "github.com/gin-gonic/gin"type RouterUser struct{}func (*RouterUser) Route(r *gin.Engine) {handler := &HandlerUser{}r.POST("/login/getCaptcha", handler.getCaptcha)
}
  • api/user/user.go;执行POST
package userimport "github.com/gin-gonic/gin"type HandlerUser struct{}func (*HandlerUser) getCaptcha(ctx *gin.Context) {ctx.JSON(200, "getCaptcha test")
}

04-发送验证码

在这里插入图片描述

  • 建立消息模型
package modeltype BusinessCode int
type Result struct {Code BusinessCode `json:"code"`Msg  string       `json:"msg"`Data any          `json:"data"`
}func (r *Result) Success(data any) *Result {r.Code = 200r.Msg = "success"r.Data = datareturn r
}func (r *Result) Fail(code BusinessCode, msg string) *Result {r.Code = coder.Msg = msgreturn r
}
  • 手机验证
package libimport "regexp"func CheckMobile(mobile string) bool {if mobile == "" {return false}regular := "^1[3-9]\\d{9}$"reg := regexp.MustCompile(regular)return reg.MatchString(mobile)
}
  • 运行状态代码
package modelconst (NoLegalMobile BusinessCode = 2001
)
  • 修改getCaptcha,返回123456为验证码
package userimport ("Gin_gPRC/lib""Gin_gPRC/model""github.com/gin-gonic/gin""log""time"
)type HandlerUser struct{}func (*HandlerUser) getCaptcha(ctx *gin.Context) {//ctx.JSON(200, "getCaptcha test")rsp := &model.Result{}//1. 获取参数mobile := ctx.PostForm("mobile")//2. 校验参数if !lib.CheckMobile(mobile) {ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))return}//3. 生成验证码code := "123456"//4. 调用短信平台接口go func() {time.Sleep(2 * time.Second)log.Println("短信平台调用成功")}()ctx.JSON(200, rsp.Success(code))
}

05-redis操作

在这里插入图片描述

  • redis.go,安装:go get github.com/go-redis/redis/v8
package daoimport ("context""github.com/go-redis/redis/v8""time"
)var Rc *RedisCache
type RedisCache struct {rdb *redis.Client
}func init() {rdb := redis.NewClient(&redis.Options{Addr:     "localhost:6379",Password: "",DB:       0,})Rc = &RedisCache{rdb: rdb}
}func (rc *RedisCache) Put(ctx context.Context, key string, value string, expire time.Duration) error {err := rc.rdb.Set(ctx, key, value, expire).Err()return err
}func (rc *RedisCache) Get(ctx context.Context, key string) (string, error) {result, err := rc.rdb.Get(ctx, key).Result()return result, err
}
  • repo/cache.go,定义Cache的接口,再由dao里的redis.go去实现
package repoimport ("context""time"
)type Cache interface {Put(ctx context.Context, key string, value string, expire time.Duration) errorGet(ctx context.Context, key string) (string, error)
}
  • user.go,加入redis保存
package userimport ("Gin_gPRC/dao""Gin_gPRC/lib""Gin_gPRC/model""Gin_gPRC/repo""context""github.com/gin-gonic/gin""log""time"
)type HandlerUser struct {cache repo.Cache
}func New() *HandlerUser {return &HandlerUser{cache: dao.Rc,}
}func (h *HandlerUser) getCaptcha(ctx *gin.Context) {//ctx.JSON(200, "getCaptcha test")rsp := &model.Result{}//1. 获取参数mobile := ctx.PostForm("mobile")//2. 校验参数if !lib.CheckMobile(mobile) {ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))return}//3. 生成验证码code := "123456"//4. 调用短信平台接口go func() {time.Sleep(2 * time.Second)log.Println("短信平台调用成功")// 制作一个超时的上下文c, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()// redis加入err := h.cache.Put(c, "REGISTER"+mobile, code, 15*time.Minute)if err != nil {log.Printf("验证码存入redis出错,%v \n", err)}}()ctx.JSON(200, rsp.Success(code))
}

06-日志

安装:go get -u go.uber.org/zap
安装:go get -u github.com/natefinch/lumberjack
在这里插入图片描述

  • logs.go
package libimport ("github.com/gin-gonic/gin""github.com/natefinch/lumberjack""go.uber.org/zap""go.uber.org/zap/zapcore""net""net/http""net/http/httputil""os""runtime/debug""strings""time"
)var lg *zap.Loggertype LogConfig struct {DebugFileName string `json:"debugFileName"`InfoFileName  string `json:"infoFileName"`WarnFileName  string `json:"warnFileName"`MaxSize       int    `json:"maxSize"`MaxAge        int    `json:"maxAge"`MaxBackups    int    `json:"maxBackups"`
}func InitLogger(cfg *LogConfig) (err error) {writeSyncerDebug := getLogWriter(cfg.DebugFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)writeSyncerInfo := getLogWriter(cfg.InfoFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)writeSyncerWarn := getLogWriter(cfg.WarnFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)encoder := getEncoder()debugCore := zapcore.NewCore(encoder, writeSyncerDebug, zapcore.DebugLevel)infoCore := zapcore.NewCore(encoder, writeSyncerInfo, zapcore.InfoLevel)warnCore := zapcore.NewCore(encoder, writeSyncerWarn, zapcore.WarnLevel)consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())std := zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel)core := zapcore.NewTee(debugCore, infoCore, warnCore, std)lg = zap.New(core, zap.AddCaller())zap.ReplaceGlobals(lg)return}func getEncoder() zapcore.Encoder {encoderConfig := zap.NewProductionEncoderConfig()encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoderencoderConfig.TimeKey = "time"encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoderencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoderencoderConfig.EncodeCaller = zapcore.ShortCallerEncoderreturn zapcore.NewJSONEncoder(encoderConfig)
}func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {lumberJackLogger := &lumberjack.Logger{Filename:   filename,MaxSize:    maxSize,MaxBackups: maxBackup,MaxAge:     maxAge,}return zapcore.AddSync(lumberJackLogger)
}func GinLogger() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()path := c.Request.URL.Pathquery := c.Request.URL.RawQueryc.Next()cost := time.Since(start)lg.Info(path,zap.Int("status", c.Writer.Status()),zap.String("method", c.Request.Method),zap.String("path", path),zap.String("query", query),zap.String("ip", c.ClientIP()),zap.String("user-agent", c.Request.UserAgent()),zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),zap.Duration("cost", cost),)}
}func GinRecovery(stack bool) gin.HandlerFunc {return func(c *gin.Context) {defer func() {if err := recover(); err != nil {var brokenPipe boolif ne, ok := err.(*net.OpError); ok {if se, ok := ne.Err.(*os.SyscallError); ok {if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {brokenPipe = true}}}httpRequest, _ := httputil.DumpRequest(c.Request, false)if brokenPipe {lg.Error(c.Request.URL.Path,zap.Any("error", err),zap.String("request", string(httpRequest)))c.Error(err.(error))c.Abort()return}if stack {lg.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),zap.String("stack", string(debug.Stack())))} else {lg.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)))}c.AbortWithStatus(http.StatusInternalServerError)}}()c.Next()}
}
  • main.go里加入log
package mainimport ("Gin_gPRC/lib""Gin_gPRC/router""context""github.com/gin-gonic/gin""log""net/http""os""os/signal""syscall""time"
)func main() {r := gin.Default()//loglc := &lib.LogConfig{DebugFileName: "./logs/debug.log",InfoFileName:  "./logs/info.log",WarnFileName:  "./logs/warn.log",MaxSize:       500,MaxAge:        28,MaxBackups:    3,}err := lib.InitLogger(lc)if err != nil {log.Fatal(err)}router.InitRouter(r)srv := &http.Server{Addr:    ":5000",Handler: r,}go func() {log.Printf("server listen at %s", srv.Addr)if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("listen: %s\n", err)}}()quit := make(chan os.Signal)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quitlog.Println("Shutdown Server ...")ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()if err := srv.Shutdown(ctx); err != nil {log.Fatal("Server Shutdown:", err)}select {case <-ctx.Done():log.Println("timeout of 2 seconds.")}log.Println("Server exiting")
}
  • 在user.go里应用
package userimport ("Gin_gPRC/dao""Gin_gPRC/lib""Gin_gPRC/model""Gin_gPRC/repo""context""github.com/gin-gonic/gin""go.uber.org/zap""time"
)type HandlerUser struct {cache repo.Cache
}func New() *HandlerUser {return &HandlerUser{cache: dao.Rc,}
}func (h *HandlerUser) getCaptcha(ctx *gin.Context) {//ctx.JSON(200, "getCaptcha test")rsp := &model.Result{}//1. 获取参数mobile := ctx.PostForm("mobile")//2. 校验参数if !lib.CheckMobile(mobile) {ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))return}//3. 生成验证码code := "123456"//4. 调用短信平台接口go func() {time.Sleep(2 * time.Second)zap.L().Info("短信平台调用成功")// 制作一个超时的上下文c, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()// redis加入err := h.cache.Put(c, "REGISTER"+mobile, code, 15*time.Minute)if err != nil {zap.L().Error("验证码存入redis出错" + err.Error())}}()ctx.JSON(200, rsp.Success(code))
}

07-配置

安装:go get github.com/spf13/viper
在这里插入图片描述

  • config.yaml
server:name: "Gin_gRPC"addr: "127.0.0.1:5000"
zap:debugFileName: "./logs/debug.log"infoFileName: "./logs/info.log"warnFileName: "./logs/warn.log"maxSize: 500,maxAge: 28,maxBackups: 3
redis:host: "localhost"port: 6379password: ""db: 0
  • config/config.go
package configimport ("Gin_gPRC/lib""github.com/go-redis/redis/v8""github.com/spf13/viper""log"
)var Conf = InitConfig()type Config struct {viper *viper.ViperSC    *ServerConfig
}type ServerConfig struct {Name stringAddr string
}func InitConfig() *Config {conf := &Config{viper: viper.New()}//workDir, _ := os.Getwd()// 确定配置文件的名称、类型与位置conf.viper.SetConfigName("config")conf.viper.SetConfigType("yaml")conf.viper.AddConfigPath("./")err := conf.viper.ReadInConfig()if err != nil {log.Fatalf("Fatal error config file: %s \n", err)}// 读取Server的配置conf.ReadServerConfig()// 初始化zapLogconf.InitZapLog()return conf
}func (c *Config) InitZapLog() {lc := &lib.LogConfig{DebugFileName: c.viper.GetString("zap.debugFileName"),InfoFileName:  c.viper.GetString("zap.infoFileName"),WarnFileName:  c.viper.GetString("zap.warnFileName"),MaxSize:       c.viper.GetInt("zap.maxSize"),MaxAge:        c.viper.GetInt("zap.maxAge"),MaxBackups:    c.viper.GetInt("zap.maxBackups"),}err := lib.InitLogger(lc)if err != nil {log.Fatal(err)}
}func (c *Config) ReadServerConfig() {sc := &ServerConfig{}sc.Name = c.viper.GetString("server.name")sc.Addr = c.viper.GetString("server.addr")c.SC = sc
}func (c *Config) ReadRedisConfig() *redis.Options {return &redis.Options{Addr:     c.viper.GetString("redis.host") + ":" + c.viper.GetString("redis.port"),Password: c.viper.GetString("redis.password"),DB:       c.viper.GetInt("redis.db"),}
}
  • 在main.go中应用
func main() {r := gin.Default()config.InitConfig()router.InitRouter(r)srv := &http.Server{Addr:    config.Conf.SC.Addr,Handler: r,}...
}
  • 在redis.go中应用
func init() {rdb := redis.NewClient(config.Conf.ReadRedisConfig())Rc = &RedisCache{rdb: rdb}
}

结束:

对于微服务,考虑尝试下使用Go-micro + Gin的方式,后续继续记录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/6478.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

麒麟操作系统服务架构保姆级教程(十四)iptables防火墙四表五链和防火墙应用案例

如果你想拥有你从未拥有过的东西&#xff0c;那么你必须去做你从未做过的事情 防火墙在运维工作中有着不可或缺的重要性。首先&#xff0c;它是保障网络安全的关键防线&#xff0c;通过设置访问控制规则&#xff0c;可精准过滤非法网络流量&#xff0c;有效阻挡外部黑客攻击、恶…

双目立体校正和Q矩阵

立体校正 对两个摄像机的图像平面重投影&#xff0c;使二者位于同一平面&#xff0c;而且左右图像的行对准。 Bouguet 该算法需要用到双目标定后外参(R&#xff0c;T) 从上图中可以看出&#xff0c;该算法主要分为两步&#xff1a; 使成像平面共面 这个办法很直观&#xff…

【C++】string类模拟实现

目录 &#x1f495;1.模拟string类构造函数 &#x1f495;2.模拟构造函数实现 &#x1f495;3.拷贝构造函数模拟实现 &#x1f495;4.析构函数模拟实现 &#x1f495;5.size函数&#xff0c;capacity函数模拟实现 &#x1f495;6.begin函数,end函数&#xff0c;模拟实…

微调Qwen2:7B模型,加入未知信息语料

对于QWen2这样的模型,在微调的时候,语料的投喂格式满足ChatML这样的格式!!! OpenAI - ChatML: 下面是ChatML格式的介绍: https://github.com/openai/openai-python/blob/release-v0.28.0/chatml.mdhttps://github.com/openai/openai-python/blob/release-v0.28.0/chat…

.Net Core微服务入门全纪录(四)——Ocelot-API网关(上)

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…

HTML根元素<html>的语言属性lang:<html lang=“en“>

诸神缄默不语-个人CSDN博文目录 在编写HTML页面时&#xff0c;通常会看到<html lang"en">这行代码&#xff0c;特别是在网页的开头部分&#xff0c;就在<!DOCTYPE html>后面。许多开发者可能对这个属性的含义不太了解&#xff0c;它到底有什么作用&…

小样本学习中的Prototypical Network(原型网络)详解

Few-shot Learning ,即“小样本学习”,是一种机器学习方法,旨在通过极少量样本训练模型,使其能够快速适应新任务或新类别。这种方法在数据稀缺的场景中非常有用。 Prototypical Network(原型网络)是小样本学习中的经典方法之一,特别适用于分类任务。它的核心思想是通过学…

mock可视化生成前端代码

介绍&#xff1a;mock是我们前后端分离的必要一环、ts、axios编写起来也很麻烦。我们就可以使用以下插件&#xff0c;来解决我们的问题。目前支持vite和webpack。&#xff08;配置超级简单&#xff01;&#xff09; 欢迎小伙伴们提issues、我们共建。提升我们的开发体验。 vi…

(回溯分割)leetcode93 复原IP地址

#include<iostream> #include<vector> #include<string> #include<algorithm> using namespace std; //卡尔的图不是按照程序执行过程而是直接画程序会执行的过程 // 实际执行是&#xff1a;n个字符&#xff0c;递推n1后&#xff08;叶子节点&#xff…

Springboot3 自动装配流程与核心文件:imports文件

注&#xff1a;本文以spring-boot v3.4.1源码为基础&#xff0c;梳理spring-boot应用启动流程、分析自动装配的原理 如果对spring-boot2自动装配有兴趣&#xff0c;可以看看我另一篇文章&#xff1a; Springboot2 自动装配之spring-autoconfigure-metadata.properties和spring…

JVM面试题解,垃圾回收之“分代回收理论”剖析

一、什么是分代回收 我们会把堆内存中的对象间隔一段时间做一次GC&#xff08;即垃圾回收&#xff09;&#xff0c;但是堆内存很大一块&#xff0c;内存布局分为新生代和老年代、其对象的特点不一样&#xff0c;所以回收的策略也应该各不相同 对于“刚出生”的新对象&#xf…

“腾讯、钉钉、飞书” 会议开源平替,免费功能强大

在数字化时代&#xff0c;远程办公和线上协作越来越火。然而&#xff0c;市面上的视频会议工具要么贵得离谱&#xff0c;要么功能受限&#xff0c;甚至还有些在数据安全和隐私保护上让人不放心。 今天开源君给大家安利一个超棒的开源项目 - Jitsi Meet&#xff0c;这可是我在网…

CNN-GRU卷积门控循环单元时间序列预测(Matlab完整源码和数据)

CNN-GRU卷积门控循环单元时间序列预测&#xff08;Matlab完整源码和数据&#xff09; 目录 CNN-GRU卷积门控循环单元时间序列预测&#xff08;Matlab完整源码和数据&#xff09;预测效果基本介绍CNN-GRU卷积门控循环单元时间序列预测一、引言1.1、研究背景与意义1.2、研究现状1…

状态模式——C++实现

目录 1. 状态模式简介 2. 代码示例 3. 单例状态对象 4. 状态模式与策略模式的辨析 1. 状态模式简介 状态模式是一种行为型模式。 状态模式的定义&#xff1a;状态模式允许对象在内部状态改变时改变它的行为&#xff0c;对象看起来好像修改了它的类。 通俗的说就是一个对象…

大华相机DH-IPC-HFW3237M支持的ONVIF协议

使用libONVIF C库。 先发现相机。 配置 lib目录 包含 编译提示缺的文件&#xff0c;到libonvif里面拷贝过来。 改UDP端口 代码 使用msvc 2022的向导生成空项目&#xff0c;从项目的main示例拷贝过来。 CameraOnvif.h #pragma once#include <QObject> #include &l…

WIN11 UEFI漏洞被发现, 可以绕过安全启动机制

近日&#xff0c;一个新的UEFI漏洞被发现&#xff0c;可通过多个系统恢复工具传播&#xff0c;微软已经正式将该漏洞标记为追踪编号“CVE-2024-7344”。根据报告的说明&#xff0c;该漏洞能让攻击者绕过安全启动机制&#xff0c;并部署对操作系统隐形的引导工具包。 据TomsH…

Kyligence AI 数据智能体:首批亮相神州数码 DC·AI 生态创新中心!

近日&#xff0c;跬智信息&#xff08;Kyligence&#xff09;长期合作伙伴神州数码&#xff0c;其 DCAI 生态创新中心正式启幕。 作为首批生态伙伴&#xff0c;Kyligence AI 数据智能体也正式入驻&#xff0c;在这里首次亮相。 Kyligence 是国内最早推出 AI 用数产品的厂商&a…

GS论文阅读--Hard Gaussian Splatting

前言 本文也是对高斯点云的分布进行优化的&#xff0c;看&#xff01; 文章目录 前言1.背景介绍2.关键内容2.1 位置梯度驱动HGS2.2 渲染误差引导HGS 3.文章贡献 1.背景介绍 在训练过程中&#xff0c;它严重依赖于视图空间位置梯度的平均幅度来增长高斯以减少渲染损失。然而&…

消息队列篇--原理篇--Pulsar(Namespace,BookKeeper,类似Kafka甚至更好的消息队列)

Apache Pulusar是一个分布式、多租户、高性能的发布/订阅&#xff08;Pub/Sub&#xff09;消息系统&#xff0c;最初由Yahoo开发并开源。它结合了Kafka和传统消息队列的优点&#xff0c;提供高吞吐量、低延迟、强一致性和可扩展的消息传递能力&#xff0c;适用于大规模分布式系…

当 Facebook 窥探隐私:用户的数字权利如何捍卫?

随着社交平台的普及&#xff0c;Facebook 已经成为全球用户日常生活的一部分。然而&#xff0c;伴随而来的隐私问题也愈发严峻。近年来&#xff0c;Facebook 频频被曝出泄露用户数据、滥用个人信息等事件&#xff0c;令公众对其隐私保护措施产生质疑。在这个信息化时代&#xf…