上一節,我們已經定義和創建了我們需要的目錄,和項目初始化。這一節我們就可以開始編寫博客配置功能了。
上面我們提到,我們的配置處理函數將存放在config
目錄中。我們的項目還需要配置文件。配置文件我們就命名為config.json
。它是一個json文件,里面將包含了博客網站的基本信息、數據庫配置信息等。
config.json
配置文件
為了方便查看和讀取config.json
,我們將它放在項目的config目錄下。它里面將包含的字段信息有:
{
"mysql": {
"database": "irisweb",
"user": "root",
"password": "123456",
"host": "localhost",
"port": 3306
},
"server": {
"site_name": "irisweb 博客",
"env": "development",
"port": 8001,
"log_level": "debug"
}
}
字段說明:
-
mysql
字段包含了連接mysql數據庫的信息。database
為數據庫名稱;user
為數據庫用戶名;password
為數據庫密碼;host
為數據庫域名或ip地址;port
為數據庫端口。 -
server
字段包含了博客網站的基本信息。site_name
為網站名稱,網站頁面會調用到;env
為博客網站的開發環境,值為development時,表示開發中,將會輸出一些開發信息供參考,值為production表示部署在生產環境中,程序將不輸出debug信息;port
為博客網站golang運行的端口,通過這個端口可以訪問到網站頁面;log_level
表示日志的記錄級別,值為debug的時候,表示記錄debug級別的信息。
讀取json文件
上面的配置文件config.json
定義好并放到config目錄后,我們還需要編寫代碼,讓golang可以讀取它,才能在項目中調用配置文件中的信息。這些文件我們都放置在config
文件夾中。
為了方便程序讀取,我們先給上面兩個字段創建兩個承載這些具體字段的結構體:
mysql.go
package config
type mysqlConfig struct {
Database string `json:"database"`
User string `json:"user"`
Password string `json:"password"`
Host string `json:"host"`
Port int `json:"port"`
Url string `json:"-"`
}
它對應的是剛才我們定義的json文件中的mysql字段。
結構體的定義是使用關鍵字 type 和 struct 來聲明一個結構體,以關鍵字 type 開始,之后是新類型的名字,最后是關鍵字 struct。
結構體里的字段都有名字,比如上面例子中的 Database 和 User 等等。如果一個字段在代碼中從來不會被用到,那可以把它命名為 _,即空標識符。
結構體變量采用大寫可以從外部訪問到,中間的string、int為這個字段的字段類型,``包含的內容為結構體字段指定一個標記信息,上面的標記表示是json字段的對應字段名稱。
結構體中的字段可以是任何類型,甚至是結構體本身,也可以是函數或者接口。可以聲明結構體類型的一個變量。
同一個包中,不能出現同名的結構體,不同包不受限制。
server.go
package config
type serverConfig struct {
SiteName string `json:"site_name"`
Env string `json:"env"`
Port int `json:"port"`
LogLevel string `json:"log_level"`
}
server.go對應的的是json文件的server字段。
config.go
package config
type configData struct {
DB mysqlConfig `json:"mysql"`
Server serverConfig `json:"server"`
}
這個表示config.json的整體結構。
用結構體解析json
解析json需要一些函數來支持,我們將這些函數都寫在config.go 里面。
定義變量
var ExecPath string
var JsonData configData
var ServerConfig serverConfig
var DB *gorm.DB
定義的這四個變量,將是后面我們博客項目中需要使用的變量。
定義執行目錄
func initPath() {
sep := string(os.PathSeparator)
//root := filepath.Dir(os.Args[0])
//ExecPath, _ = filepath.Abs(root
ExecPath, _ = os.Getwd()
length := utf8.RuneCountInString(ExecPath)
lastChar := ExecPath[length-1:]
if lastChar != sep {
ExecPath = ExecPath + sep
}
}
上面主要是獲取運行環境的目錄,來確定項目目錄,它有2種處理方法,一種是使用執行文件所在的目錄,另一種是使用執行命令時所在的目錄。
執行文件所在目錄的獲取方式是:
root := filepath.Dir(os.Args[0])
ExecPath, _ := filepath.Abs(root)
執行命令時所在目錄的獲取方式是:
ExecPath, _ := os.Getwd()
他們的應用場景有所不同,根據實際選擇使用。
為了開發中測試方便,本項目暫時使用執行時目錄。
讀取json文件
func InitJSON() {
rawConfig, err := ioutil.ReadFile("./config.json")
if err != nil {
//未初始化
fmt.Println("Invalid Config: ", err.Error())
os.Exit(-1)
}
if err := json.Unmarshal(rawConfig, &JsonData); err != nil {
fmt.Println("Invalid Config: ", err.Error())
os.Exit(-1)
}
}
讀取json函數,我們使用ioutil包來將json文件讀取到字節變量中。這里增加了判斷,如果文件不存在,則返回錯誤。接著將內容解析到結構體中,如果是一個標準的json字符串,則這里可以解析成功,如果不成功,則要檢查config.json 是否配置正確了。
解析server
func initServer() {
ServerConfig = JsonData.Server
}
將server的字段賦值給ServerConfig變量
解析mysql
func InitDB(setting *mysqlConfig) error {
var db *gorm.DB
var err error
url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
setting.User, setting.Password, setting.Host, setting.Port, setting.Database)
setting.Url = url
db, err = gorm.Open(mysql.Open(url), &gorm.Config{})
if err != nil {
return err
}
sqlDB, err := db.DB()
if err != nil {
return err
}
sqlDB.SetMaxIdleConns(1000)
sqlDB.SetMaxOpenConns(100000)
sqlDB.SetConnMaxLifetime(-1)
DB = db
return nil
}
我們在解析mysql的時候,先組裝好mysql包連接所用的連接字符串,然后通過連接字符串,使用mysql包來打開鏈接,再將mysql連接交給gorm來管理,這樣子,最終我們就可以使用gorm的orm功能了。
在連接完了之后,我們還需要做一些檢測,比如,是否連接成功。
連接成功后,嘗試選擇獲取到連接對象,給連接對象設置空閑時的最大連接數、設置與數據庫的最大打開連接數,每一個連接的生命周期等信息。
在開始的時候執行
func init() {
initPath()
//讀取json
initJSON()
//讀取server
initServer()
//初始化數據庫
err := InitDB(&JsonData.DB)
if err != nil {
fmt.Println("Failed To Connect Database: ", err.Error())
os.Exit(-1)
}
}
golang中的init函數是golang的一個特殊函數,它優先于golang的main函數執行,實現包級別的一些初始化操作。
所以,我們可以在這里初始化項目的基本信息,讓后續程序跑起來的時候可以得到設置好的配置信息。
完整的config.go
上面分步解釋了配置文件和配置文件的各個函數,這里將它組合起來成一個完整的文件。
package config
import (
"encoding/json"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"io/ioutil"
"os"
"unicode/utf8"
)
type configData struct {
DB mysqlConfig `json:"mysql"`
Server serverConfig `json:"server"`
}
func initPath() {
sep := string(os.PathSeparator)
//root := filepath.Dir(os.Args[0])
//ExecPath, _ = filepath.Abs(root
ExecPath, _ = os.Getwd()
length := utf8.RuneCountInString(ExecPath)
lastChar := ExecPath[length-1:]
if lastChar != sep {
ExecPath = ExecPath + sep
}
}
func initJSON() {
rawConfig, err := ioutil.ReadFile(fmt.Sprintf("%sconfig.json", ExecPath))
if err != nil {
//未初始化
fmt.Println("Invalid Config: ", err.Error())
os.Exit(-1)
}
if err := json.Unmarshal(rawConfig, &JsonData); err != nil {
fmt.Println("Invalid Config: ", err.Error())
os.Exit(-1)
}
}
func InitDB(setting *mysqlConfig) error {
var db *gorm.DB
var err error
url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
setting.User, setting.Password, setting.Host, setting.Port, setting.Database)
setting.Url = url
db, err = gorm.Open(mysql.Open(url), &gorm.Config{})
if err != nil {
return err
}
sqlDB, err := db.DB()
if err != nil {
return err
}
sqlDB.SetMaxIdleConns(1000)
sqlDB.SetMaxOpenConns(100000)
sqlDB.SetConnMaxLifetime(-1)
DB = db
return nil
}
func initServer() {
ServerConfig = JsonData.Server
}
var ExecPath string
var JsonData configData
var ServerConfig serverConfig
var DB *gorm.DB
func init() {
initPath()
//讀取json
initJSON()
//讀取server
initServer()
//初始化數據庫
err := InitDB(&JsonData.DB)
if err != nil {
fmt.Println("Failed To Connect Database: ", err.Error())
os.Exit(-1)
}
}
測試結果
config寫完了,我們還需要測試一下。
在根目錄執行go mod命令來將包下載下來
go mod tidy
go mod vendor
完整的項目示例代碼托管在GitHub上,需要查看完整的項目代碼可以到github.com/fesiong/goblog 上查看,也可以直接fork一份來在上面做修改。