【阅读 GinSkeleton】项目初始化

项目初始化

前置学习:

前言

gin框架他是一个小而美的一个web框架,在GitHub星有56.7k,当你学过他的源码后,你会发现他的主要功能体现在对http/net包的一个封装,让你简单更快的创建一个web服务。他并没有加载很多其他的功能,他的日志和错误都是可以替代使用的。所以在项目中你需要的很多功能,基本上都是找第三方库去使用达到你的需求。每个库都不一定是开箱即用,所以你需要一个配置去管理整个第三方库,同时进行初始化方便后续的api使用。

后面会简单介绍viper的基础使用,更主要的介绍项目中如何利用工厂模式管理整个配置以及初始化。

viper介绍

安装

go get github.com/spf13/viper
复制代码

读取配置文件

viper.SetConfigFile("./config.yaml") // 指定配置文件路径
viper.SetConfigName("config") // 配置文件名称(无扩展名)
viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则需要配置此项
viper.AddConfigPath("/etc/appname/")   // 查找配置文件所在的路径
viper.AddConfigPath("$HOME/.appname")  // 多次调用以添加多个搜索路径
viper.AddConfigPath(".")               // 还可以在工作目录中查找配置
err := viper.ReadInConfig() // 查找并读取配置文件
if err != nil { // 处理读取配置文件的错误
	panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
复制代码

写入配置文件

viper.WriteConfig() // 将当前配置写入“viper.AddConfigPath()”和“viper.SetConfigName”设置的预定义路径
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config") // 因为该配置文件写入过,所以会报错
viper.SafeWriteConfigAs("/path/to/my/.other_config")
复制代码

监控并重新读取配置文件

viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
  // 配置文件发生变更之后会调用的回调函数
	fmt.Println("Config file changed:", e.Name)
})
复制代码

确保在调用WatchConfig()之前添加了所有的配置路径。

获取特定的值

  • Get(key string) : interface{}
  • GetBool(key string) : bool
  • GetFloat64(key string) : float64
  • GetInt(key string) : int
  • GetIntSlice(key string) : []int
  • GetString(key string) : string
  • GetStringMap(key string) : map[string]interface{}
  • GetStringMapString(key string) : map[string]string
  • GetStringSlice(key string) : []string
  • GetTime(key string) : time.Time
  • GetDuration(key string) : time.Duration
  • IsSet(key string) : bool
  • AllSettings() : map[string]interface{}

反序列化

type config struct {
	Port int
	Name string
	PathMap string `mapstructure:"path_map"`
}

var C config

err := viper.Unmarshal(&C)
if err != nil {
	t.Fatalf("unable to decode into struct, %v", err)
}
复制代码

创建viper实例

x := viper.New()
y := viper.New()

//创建默认值
x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")
复制代码

创建配置文件

路径:config->config.yml/gorm_v2.yml ,

主要加入

  • 模式(调试与非调试)
  • 与http相关的
  • token配置
  • 数据库(你可以新创建一个或者写一起)
  • 日志管理
  • 其余的第三方库

具体你按照yaml的语法写即可!这个配置主要看你自己需求。

初始化代码

package bootstrap

import (
...相关库
)

// 检查项目必须的非编译目录是否存在,避免编译后调用的时候缺失相关目录
func checkRequiredFolders() {
	//1.检查配置文件是否存在
	//2.检查public目录是否存在
	//3.检查storage/logs 目录是否存在
	// 4.自动创建软连接、更好的管理静态资源
}

func init() {
	// 1. 初始化 项目根路径,参见 variable 常量包,相关路径:app\global\variable\variable.go
	//2.检查配置文件以及日志目录等非编译性的必要条件
	//3.初始化表单参数验证器,注册在容器(Web、Api共用容器)
	// 4.启动针对配置文件(confgi.yml、gorm_v2.yml)变化的监听, 配置文件操作指针,初始化为全局变量
	// config>gorm_v2.yml 启动文件变化监听事件
	// 5.初始化全局日志句柄,并载入日志钩子处理函数
	// 6.根据配置初始化 gorm mysql 全局 *gorm.Db
	// 根据配置初始化 gorm sqlserver 全局 *gorm.Db
	// 根据配置初始化 gorm postgresql 全局 *gorm.Db
	// 7.雪花算法全局变量
	// 8.websocket Hub中心启动
	// 9.casbin 依据配置文件设置参数(IsInit=1)初始化
	
}

复制代码

checkRequiredFolders提前检测配置文件是否存在,因为第三方库没有找到对应文件都进行报错,导致运行不起来或者神奇的默认操作,这样防止奇怪的bug出现。

init,只要你需要进行初始化的,都需要在这里写入,方便统一管理。至于怎么写入,这个肯定看第三方库的官方文档去进行配置初始化。

配置viper

在这里他利用工厂模式创建配置文件对象。

配置文件有两个

  • config.yml 管理常用的配置项
  • gorm_v2.yml 管理数据库相关的配置项
//app\utils\yml_config\ymlconfig_interf
// 创建工厂模式的对象的公共接口
type YmlConfigInterf interface {
	ConfigFileChangeListen() //监听文件变化
	Clone(fileName string) YmlConfigInterf // 允许 clone 一个相同功能的结构体
    //获取配置文件中的值(这个项目保证了并发安全性)
	Get(keyName string) interface{}
	GetString(keyName string) string
	GetBool(keyName string) bool
	GetInt(keyName string) int
	GetInt32(keyName string) int32
	GetInt64(keyName string) int64
	GetFloat64(keyName string) float64
	GetDuration(keyName string) time.Duration
	GetStringSlice(keyName string) []string
}

//app\utils\yml_config
var lastChangeTime time.Time
//当前包初始化函数
func init() {
	lastChangeTime = time.Now()
}

// 创建一个yaml配置文件工厂
// 参数设置为可变参数的文件名,这样参数就可以不需要传递,如果传递了多个,我们只取第一个参数作为配置文件名
func CreateYamlFactory(fileName ...string) ymlconfig_interf.YmlConfigInterf {
	//初始化viper对象
	yamlConfig := viper.New()
	// 配置文件所在目录
	yamlConfig.AddConfigPath(variable.BasePath + "/config")
	// 需要读取的文件名,默认为:config
	if len(fileName) == 0 {
		yamlConfig.SetConfigName("config")
	} else {
        //否则就是参数的第一个
		yamlConfig.SetConfigName(fileName[0])
	}
	//设置配置文件类型(后缀)为 yml
	yamlConfig.SetConfigType("yml")
	//读取配置文件
	if err := yamlConfig.ReadInConfig(); err != nil {
        //如果读取失败就返回信息。
		log.Fatal(my_errors.ErrorsConfigInitFail + err.Error())
	}
	//返回创建的对象
	return &ymlConfig{
		yamlConfig,
	}
}

type ymlConfig struct {
	viper *viper.Viper
}
复制代码

CreateYamlFactory创建具有YmlConfigInterf所有的方法的对象ymlConfig,他的成员是viper创建的对象。

使用viper

//`yml_config.CreateYamlFactory()`创建了一个项目`ymlConfig`对象,将对象赋值到全局变量中。同时开启配置文件监听。
	variable.ConfigYml = yml_config.CreateYamlFactory()
	variable.ConfigYml.ConfigFileChangeListen()
复制代码

这个项目将数据库的配置文件单独出来,所以需要进行初始化,创建相关对象。

	//这里是利用之前的对象去克隆(深拷贝)了一个配置数据库文件对象,我觉得你可以重新创建一个数据库配置文件对象
	variable.ConfigGormv2Yml = variable.ConfigYml.Clone("gorm_v2")
//。同时开启配置文件监听。
	variable.ConfigGormv2Yml.ConfigFileChangeListen()
复制代码

其余库配置

这里你可以根据第三方库得官方文件进行配置。这里推荐,你将第三方库进行一次封装成一个工厂,当需要使用相关得对象,就利用create函数去创建,这样就方便管理。

优雅退出

代码中可能没有相关的函数,其实他加载在库中。原因在这里(づ ̄3 ̄)づ╭❤~

image-20211104154639448

func init() {
	//  用于系统信号的监听
	go func() {
		//创建一个接受信息号的通道
		c := make(chan os.Signal)
		//	// kill 默认会发送 syscall.SIGTERM 信号
		//	// kill -2 发送 syscall.SIGINT 信号,我们常用的Ctrl+C就是触发系统SIGINT信号
		//	// kill -9 发送 syscall.SIGKILL 信号,但是不能被捕获,所以不需要添加它
		//	// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit
		signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM) // 监听可能的退出信号
		received := <-c                                                                           //接收信号管道中的值
		variable.ZapLog.Warn(consts.ProcessKilled, zap.String("信号值", received.String())) //打印日志
		(event_manage.CreateEventManageFactory()).FuzzyCall(variable.EventDestroyPrefix) //销毁事件
		close(c) //关闭通道
		os.Exit(1) //退出
	}()
}

复制代码

总结

从项目初始化中我们可以看到,作者将许多使用得第三方库进行了工厂封装。以viper进行举例,利用create函数去创建项目得配置文件对象,而不是直接通过库中函数去创建对象使用,如果此对象需要在全局使用,那么就将赋值到全局变量(既然在这里写的,我觉得一般都是全局变量)。

Guess you like

Origin juejin.im/post/7034095805233365023