读取配置文件
1. 主函数中增加配置初始化入口
- 先导入viper包
import (
...
"github.com/spf13/pflag"
"github.com/spf13/viper"
"log"
)
- 在 main 函数中增加了 config.Init(*cfg) 调用,用来初始化配置,cfg 变量值从命令行 flag 传入,可以传值,比如 ./apiserver -c config.yaml,也可以为空,如果为空会默认读取 conf/config.yaml。
if err:=config.Init(*cfg) ;err != nil {
panic(err)
}
- 将相应的配置改成从配置文件config.yaml(配置内容如下)读取,例如程序的端口号,ip地址,运行模式,
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
完整代码如下:
import (
...
"github.com/spf13/pflag"
"github.com/spf13/viper"
"log"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
if err:=config.Init(*cfg) ;err != nil {
panic(err)
}
// Create the Gin engine.
g := gin.New()
gin.SetMode(viper.GetString("runmode"))
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middlewares...,
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Print("The router has been deployed successfully.")
}()
log.Printf("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Printf(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
2. 解析配置的函数
- func Init(cfg string) 如果cfg不为空,加载指定配置文件,否则加载默认配置文件,并且调用监控配置文件的函数。
func Init(cfg string) error {
c := Config {
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
- func (c *Config) watchConfig通过该函数的 viper 设置,可以使 viper 监控配置文件变更,如有变更则热更新程序。所谓热更新是指:可以不重启 API 进程,使 API 加载最新配置项的值。
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Printf("Config file changed: %s", e.Name)
})
}
- func (c *Config) initConfig() error调用viper包提供的方法,读取所需要的配置
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
完整代码如下:
package config
import (
"log"
"strings"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config {
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Printf("Config file changed: %s", e.Name)
})
}
数据库连接
1.ORM框架
apiserver 用的 ORM 是 GitHub 上 star 数最多的 gorm,相较于其他 ORM,它用起来更方便,更稳定,社区也更活跃。 gorm有如下特性:
- 全功能 ORM (无限接近)
- 关联 (Has One, Has Many, Belongs To, Many To Many, 多态)
- 钩子 (在创建/保存/更新/删除/查找之前或之后)
- 预加载
- 事务
- 复合主键
- SQL 生成器
- 数据库自动迁移
- 自定义日志
- 可扩展性, 可基于 GORM 回调编写插件
- 所有功能都被测试覆盖
- 开发者友好
2.建立数据连接
1.先配置文件中,配置数据库相关参数
db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
docker_db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
- 创建数据库连接结构体,并且初始化连接
type Database struct {
Self *gorm.DB
Docker *gorm.DB
}
func (db *Database) Init() {
DB = &Database{
Self: GetSelfDB(),
Docker: GetDockerDB(),
}
}
3.根据用户名密码等参数,打开连接
func openDB(username,password,addr,name string) *gorm.DB {
config :=fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
username,
password,
addr,
name,
true,
//"Asia/Shanghai"),
"Local")
db, err := gorm.Open("mysql", config)
if err!=nil{
log.Printf("Database connection failed. Database name: %s", name)
}
setupDB(db)
return db
}