讀取配置文件
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") // 設置配置文件格式為YAMLviper.AutomaticEnv() // 讀取匹配的環境變量viper.SetEnvPrefix("APISERVER") // 讀取環境變量的前綴為APISERVERreplacer := strings.NewReplacer(".", "_") viper.SetEnvKeyReplacer(replacer)if err := viper.ReadInConfig(); err != nil { // viper解析配置文件return err}return nil
}
完整代碼如下:
package configimport ("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") // 設置配置文件格式為YAMLviper.AutomaticEnv() // 讀取匹配的環境變量viper.SetEnvPrefix("APISERVER") // 讀取環境變量的前綴為APISERVERreplacer := 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_apiserveraddr: 127.0.0.1:3306username: rootpassword: root
docker_db:name: db_apiserveraddr: 127.0.0.1:3306username: rootpassword: root
- 創建數據庫連接結構體,并且初始化連接
type Database struct {Self *gorm.DBDocker *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
}