目录

前言

前提

CLD分层理念

Gin-Web脚手架

config-全局配置信息

settings-设置配置信息

logger-日志记录

dao-数据库的配置

mysql-slqx的使用

redis-redis的使用介绍

routers-路由

controllers

logic

main.go


前言

在我的认知世界里,MVC的分层理念跟不上时代的脚步了,在前后端分离的时代。

前提

使用的工具

vcsode

go module

CLD分层理念

1.controller ---- 控制界面

2.local        ---- 逻辑处理

3.dao         ---- 数据库管理

Gin-Web脚手架

config-全局配置信息

文件类型是yaml类型

config.yaml

app:name: "bubble"mode: "dev"port: 8081log:level: "debug"filename: "bubble.log"max_size: 200max_age: 30max_backups: 7mysql:host: "127.0.0.1"port: "3306"user: "root"password: "xxxxxxxx"dbname: "sql_demo"max_open_conns: max_idle_conns:redis:host: "127.0.0.1"port: 6379password: ""db: 0pool_size: 100

为什么会存在这个文件夹?

想想呀,当去修改配置信息,比如买了新的服务器,我们需要修改主机和端口一系列需要改变的值,我们会去各个文件里面去修改配置,搜索和修改的过程就是耗时费力的。这时候,程序员的"懒惰"就发挥了作用,新建一个有配置信息的文件夹。当寻找的时候,就一目了然。

注意在Linux服务器,我们可以使用Docker容器来承载代码,可以轻松的迁移到不同的服务器上

settings-设置配置信息

viper的初始化-viper库介绍和使用

func Init() (err error) {viper.SetConfigFile("config.yaml") // 指定配置文件viper.AddConfigPath("./config/")   // 指定查找配置文件的路径err = viper.ReadInConfig()         // 读取配置信息if err != nil {                    // 读取配置信息失败return err}// 监控配置文件变化viper.WatchConfig()viper.OnConfigChange(func(in fsnotify.Event) {fmt.Println("配置文件修改了")})return
}

这个库使用的效果是事半功倍,获取配置信息。

logger-日志记录

zap的初始化-zap接收gin的默认日志

// InitLogger 初始化Logger
func Init() (err error) {writeSyncer := getLogWriter(viper.GetString("log.filename"),viper.GetInt("log.max_size"),viper.GetInt("log.max_backups"),viper.GetInt("log.max_age"),)encoder := getEncoder()var l = new(zapcore.Level)err = l.UnmarshalText([]byte(viper.GetString("log.level")))if err != nil {return}core := zapcore.NewCore(encoder, writeSyncer, l)lg := zap.New(core, zap.AddCaller())zap.ReplaceGlobals(lg) // 替换zap包中全局的logger实例,后续在其他包中只需使用zap.L()调用即可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)
}// GinLogger 接收gin框架默认的日志
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)zap.L().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),)}
}// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
func GinRecovery(stack bool) gin.HandlerFunc {return func(c *gin.Context) {defer func() {if err := recover(); err != nil {// Check for a broken connection, as it is not really a// condition that warrants a panic stack trace.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 {zap.L().Error(c.Request.URL.Path,zap.Any("error", err),zap.String("request", string(httpRequest)),)// If the connection is dead, we can't write a status to it.c.Error(err.(error)) // nolint: errcheckc.Abort()return}if stack {zap.L().Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),zap.String("stack", string(debug.Stack())),)} else {zap.L().Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),)}c.AbortWithStatus(http.StatusInternalServerError)}}()c.Next()}
}

初始化,要重写中间件去覆盖gin默认的日志

dao-数据库的配置

mysql-slqx的使用

var db *sqlx.DBfunc Init() (err error) {//fmt.Sprintf拼接函数dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True",viper.GetString("mysql.user"),viper.GetString("mysql.password"),viper.GetString("mysql.host"),viper.GetInt("mysql.port"),viper.GetString("mysql.dbname"),)// 也可以使用MustConnect连接不成功就panicdb, err = sqlx.Connect("mysql", dsn)if err != nil {zap.L().Error("connect db err", zap.Error(err))return}db.SetMaxOpenConns(viper.GetInt("mysql.max_open_conns"))db.SetMaxIdleConns(viper.GetInt("mysql.max_idle_conns"))return
}func Close (){_ = db.Close()
}

不要忘记匿名导入

 _ "github.com/go-sql-driver/mysql"

代码中使用了viper和zap库

redis-redis的使用介绍

// 声明一个全局的rdb变量
var rdb *redis.Client// 初始化连接
func Init() (err error) {rdb = redis.NewClient(&redis.Options{Addr:     fmt.Sprintf("%s:%d", viper.GetString("redis.host"),viper.GetInt("redis.port"),),Password: viper.GetString("redis.password"), // no password setDB:       viper.GetInt("redis.db"),  // use default DBPoolSize: viper.GetInt("reids.pool_size"),})_, err = rdb.Ping().Result()return
}func Close (){_ = rdb.Close()
}

配置信息在config.yaml

routers-路由

func Setup() *gin.Engine {r := gin.New()//中间件的使用r.Use(logger.GinLogger(),logger.GinRecovery(true))r.GET("/",func(c *gin.Context) {c.String(http.StatusOK,"ok")})return r;
}

controllers

根据实际的业务场景

logic

根据实际的业务场景

main.go

func main() {//加载配置if err := logger.Init(); err != nil {fmt.Printf("init settings failed,err:%v\n", err)return}//初始化日志if err := settings.Init(); err != nil {fmt.Printf("init settings failed,err:%v\n", err)return}defer zap.L().Sync()zap.L().Debug("logger init succ")//初始化Mysql数据库if err := mysql.Init(); err != nil {fmt.Printf("init settings failed,err:%v\n", err)return}mysql.Close()//初始化redisif err := redis.Init(); err != nil {fmt.Printf("init settings failed,err:%v\n", err)return}redis.Close()//注册路由r := routers.Setup()//启动服务(优雅关机)srv := &http.Server{Addr:    fmt.Sprintf(":%d", viper.GetInt("app.port")),Handler: r,}go func() {// 开启一个goroutine启动服务if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("listen: %s\n", err)}}()// 等待中断信号来优雅地关闭服务器,为关闭服务器操作设置一个5秒的超时quit := make(chan os.Signal, 1) // 创建一个接收信号的通道// kill 默认会发送 syscall.SIGTERM 信号// kill -2 发送 syscall.SIGINT 信号,我们常用的Ctrl+C就是触发系统SIGINT信号// kill -9 发送 syscall.SIGKILL 信号,但是不能被捕获,所以不需要添加它// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quitsignal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞<-quit                                               // 阻塞在此,当接收到上述两种信号时才会往下执行log.Println("Shutdown Server ...")// 创建一个5秒超时的contextctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()// 5秒内优雅关闭服务(将未处理完的请求处理完再关闭服务),超过5秒就超时退出if err := srv.Shutdown(ctx); err != nil {log.Fatal("Server Shutdown: ", err)}log.Println("Server exiting")
}

总结

基本的脚手架已经完成了,可以满足一般的需求,鉴于能力有限,后面会持续更新的。

基于gin框架的较为通用的web脚手架---CLD分层理念相关推荐

  1. go+vue——基于gin框架和gorm的web开发实战

    go+vue--基于gin框架和gorm的web开发实战 gin框架 视频.资料.笔记 安装Go环境, 添加环境变量(可能自动添加好) 下载 Go 环境变量 goland 报错: GOROOT is ...

  2. G0第25章:Gin框架进阶项目实战

    1 Gin框架源码解析 通过阅读gin框架的源码来探究gin框架路由与中间件的秘密. 1.1 Gin框架路由详解 gin框架使用的是定制版本的httprouter,其路由的原理是大量使用公共前缀的树结 ...

  3. go(gin框架)session底层使用redis实现(gorilla/sessions和gin-contrib/sessions)

    问题描述 最近在写一个基于gin框架的demo,需要实现简单的rbac,在网上找了一个有基础功能的基本框架,Go Gin Example,框架本身集成了基本功能:日志,mysql增删改查,路由,jwt ...

  4. golang gin框架入门教程,gin框架脚手架,开箱即用

    gin-layout github地址:https://github.com/wannanbigpig/gin-layout Gin Project Template 本项目使用 gin 框架为核心搭 ...

  5. GinCMS 使用golang Gin框架xorm开发的小型内容管理系统

    系统介绍 服务器端:使用GoLang,基于Gin框架.MySQL数据库.用到的组件xorm. 前端展示:使用基于LayUI的layuicms.用到的第三方组件authtree.treeTable. G ...

  6. go语言微服务项目,高级篇--03go-mirco框架-gin框架-mvc-REST-Session

    go-Micro 框架 创建 micro 服务 命令:micro new --type srv test66 框架默认自带服务发现:mdns. 使用consul服务发现: 1. 初始consul服务发 ...

  7. 基于Stripes框架进行Java Web开发

    Mark Eagle是美国乔治亚州亚特兰大市MATRIX Resources有限公司的一位资深软件工程师,拥有Sun公司的SCP和SCWCD认证.Mark本人非常喜欢使用开源软件进行软件开发,并且多次 ...

  8. Java Web 程序设计----基于SSM框架(正在更新中)

    Java Web 程序设计----基于SSM框架 提示:主要用于个人学习.复习.查阅等. 文章目录 Java Web 程序设计----基于SSM框架 一.网页前端开发基础 HTML文档结构 提示:以下 ...

  9. 苹果树病虫智能识别的web部署,基于django框架

    苹果树病虫智能识别的web部署,基于django框架 本WebApp是基于django框架来实现对苹果树病虫智能识别App的web部署 苹果树病虫智能识别App请参考人工智能 机器学习中的相关文章 用 ...

最新文章

  1. 和达摩院深度绑定,阿里云下一个十年,成为“云上的阿里巴巴”
  2. Java中的深浅拷贝问题你清楚吗?
  3. 解读ADC采样芯片(EV10AQ190A)的工作模式(四通道模式)
  4. thinkphp日志泄漏漏洞_ThinkPHP框架通杀所有版本的一个SQL注入漏洞详细分析及测试方法...
  5. outlookbar control
  6. DDPG-强化学习算法
  7. 做靠谱的程序员--《程序员修炼之道》读书报告
  8. android handler.removeCallbacksAndMessages(null)的使用
  9. mac os mysql 命令_Mac环境下MySQL的安装和基本命令的使用
  10. 线性代数导论2——矩阵消元
  11. android fragment 管理器,Android Fragment 與 Fragment管理器
  12. 【Python入门教程】第70篇 创建文本文件
  13. java 判断客户端是手机端还是PC端(SSH框架)
  14. 世纪佳缘与百合网结婚了!网络婚恋进入2.0时代 ?
  15. MATLAB报错“现在无法访问以前可以访问的文件”
  16. 流程表结构设计第一版
  17. 深入理解读写锁ReentrantReadWriteLock
  18. [linux命令]查找包含指定内容的文件
  19. 你真的会解决android ANR 问题吗?
  20. ddr2是几代内存_如何区分DDR1 DDR2 DDR3内存条

热门文章

  1. Manacher算法总结
  2. SpringBoot的CRUD
  3. wsus下游服务器状态,wsus服务器提示SelfUpdate服务没有运行
  4. BT下载器Folx是怎么管理大量下载任务的
  5. 如何革命社交媒体、实现去中心化?丝绸之路创始人在狱中提出了构想
  6. Unity 热更新方案之——ILRuntime
  7. 监控易:超大规模IT设备监控性能运维挑战如何破?
  8. 房子真烦,事情多多,小议大事.............
  9. 【Python自学笔记】快速入手PyScript,让Python代码优雅的运行于HTML
  10. python学习测试题3及参考答案