yyz notes yyz notes
首页
  • RBAC权限设计
  • 架构图标设计
  • 账号体系
  • python基础
  • python高级
  • python模块
  • python设计模式
  • python数据结构与算法
  • django
  • django-DRF
  • flask
  • 直接设计开源pip包
  • 直接设计开源项目
  • python示例题/脚本
  • python面试题
  • golang基础
  • golang高级
  • golang常用组件
  • gin框架
  • es6
  • javascript
  • react
  • vue
  • TypeScript
  • mysql
  • redis
  • minio
  • elasticsearch
  • mongodb
  • 消息队列
  • 自动化测试
  • 操作系统

    • linux
    • windows
  • nginx
  • docker
  • k8s
  • git
  • ldap
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

益章

可乐鸡翅
首页
  • RBAC权限设计
  • 架构图标设计
  • 账号体系
  • python基础
  • python高级
  • python模块
  • python设计模式
  • python数据结构与算法
  • django
  • django-DRF
  • flask
  • 直接设计开源pip包
  • 直接设计开源项目
  • python示例题/脚本
  • python面试题
  • golang基础
  • golang高级
  • golang常用组件
  • gin框架
  • es6
  • javascript
  • react
  • vue
  • TypeScript
  • mysql
  • redis
  • minio
  • elasticsearch
  • mongodb
  • 消息队列
  • 自动化测试
  • 操作系统

    • linux
    • windows
  • nginx
  • docker
  • k8s
  • git
  • ldap
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • golang基础

  • golang高级

  • 常用组件

  • gin

    • gin基础

    • gin源码解析

    • gin日志配置

    • 脚手架

      • CLD模式
        • 链接资料
          • _00.项目结构_
          • [#](http://v5blog.cn/pages/6829fb/#0-1-%E6%96%B0%E5%BB%BA%E9%A1%B9%E7%9B%AE) 0.1 新建项目_
          • [#](http://v5blog.cn/pages/6829fb/#0-2-%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E5%9B%BE) 0.2 项目结构图_
          • [#](http://v5blog.cn/pages/6829fb/#0-3-%E5%88%9D%E5%A7%8B%E5%8C%96main-go) 0.3 初始化main.go_
          • [#](http://v5blog.cn/pages/6829fb/#01-%E7%AC%AC%E4%B8%80-%E5%8A%A0%E8%BD%BD%E9%85%8D%E7%BD%AE) 01.第一: 加载配置_
          • [#](http://v5blog.cn/pages/6829fb/#1-1-config-yaml) 1.1 config.yaml_
          • [#](http://v5blog.cn/pages/6829fb/#1-2-settings-settings-go) 1.2 settings/settings.go_
          • [#](http://v5blog.cn/pages/6829fb/#1-3-main-go) 1.3 main.go_
          • [#](http://v5blog.cn/pages/6829fb/#02-%E7%AC%AC%E4%BA%8C-%E5%88%9D%E5%A7%8B%E5%8C%96%E6%97%A5%E5%BF%97) 02.第二: 初始化日志_
          • [#](http://v5blog.cn/pages/6829fb/#2-1-logger-logger-go) 2.1 logger/logger.go_
          • [#](http://v5blog.cn/pages/6829fb/#2-2-main-go) 2.2 main.go_
          • [#](http://v5blog.cn/pages/6829fb/#03-%E7%AC%AC%E4%B8%89-%E5%88%9D%E5%A7%8B%E5%8C%96mysql%E8%BF%9E%E6%8E%A5) 03.第三: 初始化mysql连接_
          • [#](http://v5blog.cn/pages/6829fb/#3-1-dao-mysql-mysql-go) 3.1 dao/mysql/mysql.go_
          • [#](http://v5blog.cn/pages/6829fb/#3-2-main-go) 3.2 main.go_
          • [#](http://v5blog.cn/pages/6829fb/#04-%E5%88%9D%E5%A7%8B%E5%8C%96redis%E8%BF%9E%E6%8E%A5) 04.初始化redis连接_
          • [#](http://v5blog.cn/pages/6829fb/#4-1-dao-redis-redis-go) 4.1 dao/redis/redis.go_
          • [#](http://v5blog.cn/pages/6829fb/#4-2-mian-go) 4.2 mian.go_
          • [#](http://v5blog.cn/pages/6829fb/#05-%E6%B3%A8%E5%86%8C%E8%B7%AF%E7%94%B1) 05.注册路由_
          • [#](http://v5blog.cn/pages/6829fb/#5-1-routes-routes-go) 5.1 routes/routes.go_
          • [#](http://v5blog.cn/pages/6829fb/#5-2-main-go) 5.2 main.go_
          • [#](http://v5blog.cn/pages/6829fb/#06-%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1-%E4%BC%98%E9%9B%85%E5%85%B3%E6%9C%BA) 06.启动服务(优雅关机)_
          • [#](http://v5blog.cn/pages/6829fb/#6-1-main-go) 6.1 main.go_
          • [#](http://v5blog.cn/pages/6829fb/#6-2-%E5%90%AF%E5%8A%A8%E6%B5%8B%E8%AF%95) 6.2 启动测试_
          • [#](http://v5blog.cn/pages/6829fb/#6-3-%E4%BD%BF%E7%94%A8golang-ide%E5%90%AF%E5%8A%A8) 6.3 使用golang ide启动_
    • 优雅地关机或重启gin项目
    • restful api
    • 限流操作
    • 大型WEB项目CLD分层
  • golang
  • gin
  • 脚手架
YiZhang-You
2023-05-24
目录

CLD模式

# CLD模式

# 链接资料

  1. 博客

08.web脚手架 | 不做大哥好多年 (opens new window)

# 00.项目结构

# # (opens new window) 0.1 新建项目

# # (opens new window) 0.2 项目结构图

CLD + 模型

  • controller

  • logic

  • dao

  • models

日志、路由、三方库

  • logger

  • routes

  • pkg

main.go、配置文件

  • main.go

  • conf

  • settings

# # (opens new window) 0.3 初始化main.go

func main() {
	// 1. 加载配置(viper配置管理)

	// 2. 初始化日志(zap日志库)

	// 3. 初始化MySQL连接(sqlx)

	// 4. 初始化Redis连接(go-redis)

	// 5. 注册路由

	// 6. 启动服务(优雅关机)

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

1234567891011121314

# # (opens new window) 01.第一: 加载配置

# # (opens new window) 1.1 config.yaml

name: "web_app"
mode: "dev"
port: 8080
version: "v0.1.4"

# 雪花算法:开始时间 机器ID
start_time: "2020-07-01"
machine_id: 1

log:
  level: "debug"
  filename: "web_app.log"
  max_size: 200
  max_age: 30
  max_backups: 7
mysql:
  host: "127.0.0.1"
  port: 3306
  user: "root"
  password: "chnsys@2016"
  dbname: "gin_bbs"
  max_open_conns: 200
  max_idle_conns: 50
redis:
  host: "127.0.0.1"
  port: 6379
  password: ""
  db: 0
  pool_size: 100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

1234567891011121314151617181920212223242526272829

# # (opens new window) 1.2 settings/settings.go

package settings

import (
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

// Conf 全局变量,用来保存程序的所有配置信息
var Conf = new(AppConfig)

type AppConfig struct {
	Name         string `mapstructure:"name"`
	Mode         string `mapstructure:"mode"`
	Version      string `mapstructure:"version"`
	Port         int    `mapstructure:"port"`
	StartTime    string `mapstructure:"start_time"`
	MachineID    int64  `mapstructure:"machine_id"`
	*LogConfig   `mapstructure:"log"`
	*MySQLConfig `mapstructure:"mysql"`
	*RedisConfig `mapstructure:"redis"`
}

type LogConfig struct {
	Level      string `mapstructure:"level"`
	Filename   string `mapstructure:"filename"`
	MaxSize    int    `mapstructure:"max_size"`
	MaxAge     int    `mapstructure:"max_age"`
	MaxBackups int    `mapstructure:"max_backups"`
}

type MySQLConfig struct {
	Host         string `mapstructure:"host"`
	User         string `mapstructure:"user"`
	Password     string `mapstructure:"password"`
	DbName       string `mapstructure:"dbname"`
	Port         int    `mapstructure:"port"`
	MaxOpenConns int    `mapstructure:"max_open_conns"`
	MaxIdleConns int    `mapstructure:"max_idle_conns"`
}

type RedisConfig struct {
	Host     string `mapstructure:"host"`
	Password string `mapstructure:"password"`
	Port     int    `mapstructure:"port"`
	DB       int    `mapstructure:"db"`
	PoolSize int    `mapstructure:"pool_size"`
}

func Init() (err error) {

	viper.SetConfigFile("conf/config.yaml")
	//viper.SetConfigName("config") // 指定配置文件名称(不需要带后缀)
	//viper.SetConfigType("yaml")   // 指定配置文件类型(专用于从远程获取配置信息时指定配置文件类型的)
	viper.AddConfigPath(".")   // 指定查找配置文件的路径(这里使用相对路径)
	err = viper.ReadInConfig() // 读取配置信息
	if err != nil {
		// 读取配置信息失败
		fmt.Printf("viper.ReadInConfig() failed, err:%v\n", err)
		return
	}
	// 把读取到的配置信息反序列化到 Conf 变量中
	if err := viper.Unmarshal(Conf); err != nil {
		fmt.Printf("viper.Unmarshal failed, err:%v\n", err)
	}
	viper.WatchConfig()
	viper.OnConfigChange(func(in fsnotify.Event) {
		fmt.Println("配置文件修改了,重新加载到全局Conf ...")
		if err := viper.Unmarshal(Conf); err != nil {
			fmt.Printf("viper.Unmarshal failed, err:%v\n", err)
		}
	})
	return
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374

# # (opens new window) 1.3 main.go

package main

import (
	"fmt"
	"my_web/settings"
)

func main() {
	// 1. 加载配置(viper配置管理)
	if err := settings.Init(); err != nil {
		fmt.Printf("init settings failed, err:%v\n", err)
		return
	}
	fmt.Println(settings.Conf)
	fmt.Println(settings.Conf.Name)  // web_app
	fmt.Println(settings.Conf.LogConfig == nil)
	// 2. 初始化日志(zap日志库)

	// 3. 初始化MySQL连接(sqlx)

	// 4. 初始化Redis连接(go-redis)

	// 5. 注册路由

	// 6. 启动服务(优雅关机)

}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

12345678910111213141516171819202122232425262728

# # (opens new window) 02.第二: 初始化日志

# # (opens new window) 2.1 logger/logger.go

package logger

import (
	"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"
	"web_app/settings"
)

var lg *zap.Logger

// Init 初始化lg
func Init(cfg *settings.LogConfig, mode string) (err error) {
	writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
	encoder := getEncoder()
	var l = new(zapcore.Level)
	err = l.UnmarshalText([]byte(cfg.Level))
	if err != nil {
		return
	}
	var core zapcore.Core
	if mode == "dev" {
		// 进入开发模式,日志输出到终端
		consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
		core = zapcore.NewTee(
			zapcore.NewCore(encoder, writeSyncer, l),
			zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),
		)
	} else {
		// 如果不是dev模式,就记录日志到日志文件中
		core = zapcore.NewCore(encoder, writeSyncer, l)
	}

	lg = zap.New(core, zap.AddCaller())

	zap.ReplaceGlobals(lg)
	zap.L().Info("init logger success")
	return
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.TimeKey = "time"
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
	encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
	return 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.Path
		query := c.Request.URL.RawQuery
		c.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),
		)
	}
}

// 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 bool
				if 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)),
					)
					// If the connection is dead, we can't write a status to it.
					c.Error(err.(error)) // nolint: errcheck
					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()
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136

# # (opens new window) 2.2 main.go

package main

import (
	"fmt"
	"go.uber.org/zap"
	"my_web/logger"
	"my_web/settings"
)

func main() {
	// 1. 加载配置(viper配置管理)
	if err := settings.Init(); err != nil {
		fmt.Printf("init settings failed, err:%v\n", err)
		return
	}
	fmt.Println(settings.Conf)
	fmt.Println(settings.Conf.Name) // web_app
	fmt.Println(settings.Conf.LogConfig == nil)
	// 2. 初始化日志(zap日志库)
	if err := logger.Init(settings.Conf.LogConfig, settings.Conf.Mode); err != nil {
		fmt.Printf("init logger failed, err:%v\n", err)
		return
	}
	defer zap.L().Sync()
	zap.L().Debug("logger init success...")
	// 3. 初始化MySQL连接(sqlx)

	// 4. 初始化Redis连接(go-redis)

	// 5. 注册路由

	// 6. 启动服务(优雅关机)

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

12345678910111213141516171819202122232425262728293031323334

# # (opens new window) 03.第三: 初始化mysql连接

# # (opens new window) 3.1 dao/mysql/mysql.go

package mysql

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"go.uber.org/zap"
	"web_app/settings"
)

var db *sqlx.DB

func Init(cfg *settings.MySQLConfig) (err error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True",
		cfg.User,
		cfg.Password,
		cfg.Host,
		cfg.Port,
		cfg.DbName,
	)
	// 也可以使用MustConnect连接不成功就panic
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		zap.L().Error("connect DB failed", zap.Error(err))
		return
	}
	db.SetMaxOpenConns(cfg.MaxOpenConns)
	db.SetMaxIdleConns(cfg.MaxIdleConns)
	return
}

func Close() {
	_ = db.Close()
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

12345678910111213141516171819202122232425262728293031323334

# # (opens new window) 3.2 main.go

package main

import (
	"fmt"
	"go.uber.org/zap"
	"my_web/dao/mysql"
	"my_web/logger"
	"my_web/settings"
)

func main() {
	// 1. 加载配置(viper配置管理)
	if err := settings.Init(); err != nil {
		fmt.Printf("init settings failed, err:%v\n", err)
		return
	}
	fmt.Println(settings.Conf)
	fmt.Println(settings.Conf.Name) // web_app
	fmt.Println(settings.Conf.LogConfig == nil)
	// 2. 初始化日志(zap日志库)
	if err := logger.Init(settings.Conf.LogConfig, settings.Conf.Mode); err != nil {
		fmt.Printf("init logger failed, err:%v\n", err)
		return
	}
	defer zap.L().Sync()
	zap.L().Debug("logger init success...")
	// 3. 初始化MySQL连接(sqlx)
	if err := mysql.Init(settings.Conf.MySQLConfig); err != nil {
		fmt.Printf("init mysql failed, err:%v\n", err)
		return
	}
	defer mysql.Close()
	// 4. 初始化Redis连接(go-redis)

	// 5. 注册路由

	// 6. 启动服务(优雅关机)

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

123456789101112131415161718192021222324252627282930313233343536373839

# # (opens new window) 04.初始化redis连接

# # (opens new window) 4.1 dao/redis/redis.go

package redis
import (
	"fmt"
	"web_app/settings"
	"github.com/go-redis/redis"
)

// 声明一个全局的rdb变量
var rdb *redis.Client

// Init 初始化连接
func Init(cfg *settings.RedisConfig) (err error) {
	rdb = redis.NewClient(&redis.Options{
		Addr: fmt.Sprintf("%s:%d",
			cfg.Host,
			cfg.Port,
		),
		Password: cfg.Password, // no password set
		DB:       cfg.DB,       // use default DB
		PoolSize: cfg.PoolSize,
	})

	_, err = rdb.Ping().Result()
	return
}

func Close() {
	_ = rdb.Close()
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

1234567891011121314151617181920212223242526272829

# # (opens new window) 4.2 mian.go

package main

import (
	"fmt"
	"go.uber.org/zap"
	"my_web/dao/mysql"
	"my_web/dao/redis"
	"my_web/logger"
	"my_web/settings"
)

func main() {
	// 1. 加载配置(viper配置管理)
	if err := settings.Init(); err != nil {
		fmt.Printf("init settings failed, err:%v\n", err)
		return
	}
	fmt.Println(settings.Conf)
	fmt.Println(settings.Conf.Name) // web_app
	fmt.Println(settings.Conf.LogConfig == nil)
	// 2. 初始化日志(zap日志库)
	if err := logger.Init(settings.Conf.LogConfig, settings.Conf.Mode); err != nil {
		fmt.Printf("init logger failed, err:%v\n", err)
		return
	}
	defer zap.L().Sync()
	zap.L().Debug("logger init success...")
	// 3. 初始化MySQL连接(sqlx)
	if err := mysql.Init(settings.Conf.MySQLConfig); err != nil {
		fmt.Printf("init mysql failed, err:%v\n", err)
		return
	}
	defer mysql.Close()
	// 4. 初始化Redis连接(go-redis)
	if err := redis.Init(settings.Conf.RedisConfig); err != nil {
		fmt.Printf("init redis failed, err:%v\n", err)
		return
	}
	defer redis.Close()
	// 5. 注册路由

	// 6. 启动服务(优雅关机)

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

1234567891011121314151617181920212223242526272829303132333435363738394041424344

# # (opens new window) 05.注册路由

# # (opens new window) 5.1 routes/routes.go

package routes
import (
	"net/http"
	"web_app/logger"
	"web_app/settings"
	"github.com/gin-gonic/gin"
)

func Setup(mode string) *gin.Engine {
	if mode == gin.ReleaseMode {
		gin.SetMode(gin.ReleaseMode)
	}
	r := gin.New()
	r.Use(logger.GinLogger(), logger.GinRecovery(true))

	r.GET("/version", func(c *gin.Context) {
		c.String(http.StatusOK, settings.Conf.Version)
	})
	return r
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

1234567891011121314151617181920

# # (opens new window) 5.2 main.go

package main

import (
	"fmt"
	"go.uber.org/zap"
	"my_web/dao/mysql"
	"my_web/dao/redis"
	"my_web/logger"
	"my_web/routes"
	"my_web/settings"
)

func main() {
	// 1. 加载配置(viper配置管理)
	if err := settings.Init(); err != nil {
		fmt.Printf("init settings failed, err:%v\n", err)
		return
	}
	fmt.Println(settings.Conf)
	fmt.Println(settings.Conf.Name) // web_app
	fmt.Println(settings.Conf.LogConfig == nil)
	// 2. 初始化日志(zap日志库)
	if err := logger.Init(settings.Conf.LogConfig, settings.Conf.Mode); err != nil {
		fmt.Printf("init logger failed, err:%v\n", err)
		return
	}
	defer zap.L().Sync()
	zap.L().Debug("logger init success...")
	// 3. 初始化MySQL连接(sqlx)
	if err := mysql.Init(settings.Conf.MySQLConfig); err != nil {
		fmt.Printf("init mysql failed, err:%v\n", err)
		return
	}
	defer mysql.Close()
	// 4. 初始化Redis连接(go-redis)
	if err := redis.Init(settings.Conf.RedisConfig); err != nil {
		fmt.Printf("init redis failed, err:%v\n", err)
		return
	}
	defer redis.Close()
	// 5. 注册路由
	r := routes.Setup(settings.Conf.Mode)
	err := r.Run(fmt.Sprintf(":%d", settings.Conf.Port))
	if err != nil {
		fmt.Printf("run server failed, err:%v\n", err)
		return
	}
	// 6. 启动服务(优雅关机)

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

# # (opens new window) 06.启动服务(优雅关机)

# # (opens new window) 6.1 main.go

package main

import (
	"context"  // 这个包需要手动的导入
	"fmt"
	"go.uber.org/zap"
	"log"
	"my_web/dao/mysql"
	"my_web/dao/redis"
	"my_web/logger"
	"my_web/routes"
	"my_web/settings"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	// 1. 加载配置(viper配置管理)
	if err := settings.Init(); err != nil {
		fmt.Printf("init settings failed, err:%v\n", err)
		return
	}
	fmt.Println(settings.Conf)
	fmt.Println(settings.Conf.Name) // web_app
	fmt.Println(settings.Conf.LogConfig == nil)
	// 2. 初始化日志(zap日志库)
	if err := logger.Init(settings.Conf.LogConfig, settings.Conf.Mode); err != nil {
		fmt.Printf("init logger failed, err:%v\n", err)
		return
	}
	defer zap.L().Sync()
	zap.L().Debug("logger init success...")
	// 3. 初始化MySQL连接(sqlx)
	if err := mysql.Init(settings.Conf.MySQLConfig); err != nil {
		fmt.Printf("init mysql failed, err:%v\n", err)
		return
	}
	defer mysql.Close()
	// 4. 初始化Redis连接(go-redis)
	if err := redis.Init(settings.Conf.RedisConfig); err != nil {
		fmt.Printf("init redis failed, err:%v\n", err)
		return
	}
	defer redis.Close()
	// 5. 注册路由
	// 5. 注册路由
	r := routes.Setup(settings.Conf.Mode)
	r.Run()
	// 6. 启动服务(优雅关机)
	fmt.Println(settings.Conf.Port)
	srv := &http.Server{
		Addr:    fmt.Sprintf(":%d", settings.Conf.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 信号转发给quit
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞
	<-quit                                               // 阻塞在此,当接收到上述两种信号时才会往下执行
	zap.L().Info("Shutdown Server ...")
	// 创建一个5秒超时的context
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	// 5秒内优雅关闭服务(将未处理完的请求处理完再关闭服务),超过5秒就超时退出
	if err := srv.Shutdown(ctx); err != nil {
		zap.L().Fatal("Server Shutdown", zap.Error(err))
	}

	zap.L().Info("Server exiting")
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384

# # (opens new window) 6.2 启动测试

  • http://127.0.0.1:8080/version

  • 修改 config.yaml 中的version配置,保存后就会在页面看到自动修改

# # (opens new window) 6.3 使用golang ide启动

编辑 (opens new window)  (opens new window)

上次

编辑 (opens new window)
gin框架日志配置 zap
优雅地关机或重启gin项目

← gin框架日志配置 zap 优雅地关机或重启gin项目→

最近更新
01
配置yun源
05-24
02
linux-配置python虚拟环境
05-24
03
linux文件目录管理
05-24
更多文章>
Theme by Vdoing | Copyright © 2023-2023 yizhang | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式