濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > 使用Go語(yǔ)言實(shí)現(xiàn)配置文件熱加載功能

使用Go語(yǔ)言實(shí)現(xiàn)配置文件熱加載功能

熱門(mén)標(biāo)簽:阿里云ai電話機(jī)器人 釘釘有地圖標(biāo)注功能嗎 濱州自動(dòng)電銷(xiāo)機(jī)器人排名 惠州電銷(xiāo)防封電話卡 浙江高頻外呼系統(tǒng)多少錢(qián)一個(gè)月 汕頭小型外呼系統(tǒng) 建造者2地圖標(biāo)注 鄭州亮點(diǎn)科技用的什么外呼系統(tǒng) 黃岡人工智能電銷(xiāo)機(jī)器人哪個(gè)好

 說(shuō)到配置文件熱加載,這個(gè)功能在很多框架中都提供了,如beego,實(shí)現(xiàn)的效果就是當(dāng)你修改文件后,會(huì)把你修改后的配置重新加載到配置文件中,而不用重啟程序,這個(gè)功能在日常中還是非常實(shí)用的,畢竟很多時(shí)候,線上的配置文件不是想改就能改的。

這次就自己實(shí)現(xiàn)一個(gè)配置文件的熱加載功能的包,并通過(guò)一個(gè)簡(jiǎn)單的例子對(duì)完成的包進(jìn)行使用驗(yàn)證

配置文件熱加載包的是實(shí)現(xiàn)

其實(shí)整體的思路還是比較簡(jiǎn)單的,當(dāng)獲取配置文件內(nèi)容后,會(huì)開(kāi)啟一個(gè)goroutine,去 循環(huán)讀配置文件,當(dāng)然這里不可能不限制的一直循環(huán),而是設(shè)置了一個(gè)定時(shí)器,定時(shí)去讀文件,根據(jù)文件的修改時(shí)間是否變化,從而確定是否重新reload配置文件

實(shí)現(xiàn)的config 包的文件結(jié)構(gòu)為:

├── config.go
└── config_notify.go

config.go:代碼的主要處理邏輯
config_notify.go:主要定義了一個(gè)接口,用于當(dāng)文件修改時(shí)間變化的時(shí)候執(zhí)行回調(diào)

config_notify.go的代碼相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,我們先看看這個(gè)代碼:

package config
// 定義一個(gè)通知的接口
type Notifyer interface {
 Callback(*Config)
}

這樣當(dāng)我們實(shí)現(xiàn)了Callback這個(gè)方法的時(shí)候,我們就實(shí)現(xiàn)了Notifyer這個(gè)接口,具體的調(diào)用在后面會(huì)說(shuō)

在config.go中我們頂一個(gè)了一個(gè)結(jié)構(gòu)體:

type Config struct {
 filename string
 lastModifyTime int64
 data map[string]string
 rwLock sync.RWMutex
 notifyList []Notifyer
}

結(jié)構(gòu)體中主要包含幾個(gè)字段:

filename:配置文件名字
lastModifyTime:配置文件的最后修改時(shí)間
data:用于將從配置文件中讀取的內(nèi)容存儲(chǔ)為map
rwlock:讀寫(xiě)鎖
notifyList:用于將調(diào)用該包的程序追加到切片中,用于通知調(diào)用上面在config_notify.go定義的callback回調(diào)函數(shù)

關(guān)于讀取配置文件中的內(nèi)容并存儲(chǔ)到map中,這里定義了一個(gè)方法實(shí)現(xiàn):

func (c *Config) parse()(m map[string]string,err error){
 // 讀文件并或?qū)⑽募械臄?shù)據(jù)以k/v的形式存儲(chǔ)到map中
 m = make(map[string]string,1024)
 file,err := os.Open(c.filename)
 if err != nil{
  return
 }
 var lineNo int
 reader := bufio.NewReader(file)
 for{
  // 一行行的讀文件
  line,errRet := reader.ReadString('\n')
  if errRet == io.EOF{
   // 表示讀到文件的末尾
   break
  }
  if errRet != nil{
   // 表示讀文件出問(wèn)題
   err = errRet
   return
  }
  lineNo++
  line = strings.TrimSpace(line) // 取出空格
  if len(line) == 0 || line[0] == '\n' || line[0] == '+' || line[0] == ';'{
   // 當(dāng)前行為空行或者是注釋行等
   continue
  }
  arr := strings.Split(line,"=") // 通過(guò)=進(jìn)行切割取出k/v結(jié)構(gòu)
  if len(arr) == 0{
   fmt.Printf("invalid config,line:%d\n",lineNo)
   continue
  }
  key := strings.TrimSpace(arr[0])
  if len(key) == 0{
   fmt.Printf("invalid config,line:%d\n",lineNo)
   continue
  }
  if len(arr) == 1{
   m[key] = ""
   continue
  }
  value := strings.TrimSpace(arr[1])
  m[key] = value
 }
 return
}

而最后我們就需要一個(gè)定時(shí)器,每隔一段時(shí)間判斷配置文件的最后修改時(shí)間是否變化,如果變化則重新讀取一次文件并將文件內(nèi)容存儲(chǔ)到map中。

func (c *Config) reload(){
 // 這里啟動(dòng)一個(gè)定時(shí)器,每5秒重新加載一次配置文件
 ticker := time.NewTicker(time.Second*5)
 for _ = range ticker.C{
  func(){
   file,err := os.Open(c.filename)
   if err != nil{
    fmt.Printf("open %s failed,err:%v\n",c.filename,err)
    return
   }
   defer file.Close()
   fileInfo,err := file.Stat()
   if err != nil{
    fmt.Printf("stat %s failed,err:%v\n",c.filename,err)
    return
   }
   curModifyTime := fileInfo.ModTime().Unix()
   fmt.Printf("%v --- %v\n",curModifyTime,c.lastModifyTime)
   //判斷文件的修改時(shí)間是否大于最后一次修改時(shí)間
   if curModifyTime > c.lastModifyTime{
    m,err := c.parse()
    if err != nil{
     fmt.Println("parse failed,err:",err)
     return
    }
    c.rwLock.Lock()
    c.data = m
    c.rwLock.Unlock()
    for _, n:=range c.notifyList{
     n.Callback(c)
    }
    c.lastModifyTime = curModifyTime
   }
  }()
 }

關(guān)于config完整的代碼地址:https://github.com/pythonsite/go_simple_code/tree/master/config

一個(gè)演示上述包的例子

這里一個(gè)簡(jiǎn)單的例子,代碼的邏輯也非常簡(jiǎn)單就是寫(xiě)一個(gè)循環(huán)從配置文件讀取配置信息,當(dāng)然這里是為了測(cè)試效果,寫(xiě)成了循環(huán)。這里有個(gè)問(wèn)題需要注意,就是在配置文件中存放數(shù)據(jù)的時(shí)候應(yīng)該是如下格式存儲(chǔ)

listen_addr = localhost
server_port = 1000
# Nginx addr
nginx_addr = 192.168.1.2:9090

測(cè)試代碼的主要結(jié)構(gòu)如下:

├── config.conf
└── main.go

config.conf為配置文件
main.go 為主要測(cè)試代碼

type AppConfig struct {
 port int
 nginxAddr string
}
type AppconfigMgr struct {
 config atomic.Value
}
var appConfigMgr = AppconfigMgr{}
func(a *AppconfigMgr)Callback(conf *config.Config){
 var appConfig = AppConfig{}
 port,err := conf.GetInt("server_port")
 if err != nil{
  fmt.Println("get port failed,err:",err)
  return
 }
 appConfig.port = port
 fmt.Println("port:",appConfig.port)
 nginxAddr,err := conf.GetString("nginx_addr")
 if err != nil{
  fmt.Println("get nginx addr failed,err:",err)
  return
 }
 appConfig.nginxAddr = nginxAddr
 fmt.Println("nginx addr :",appConfig.nginxAddr)
 appConfigMgr.config.Store(appConfig)
}
func run(){
 for {
  // 每5秒打印一次數(shù)據(jù),查看自己更改配置文件后是否可以熱刷新
  appConfig := appConfigMgr.config.Load().(*AppConfig)
  fmt.Println("port:",appConfig.port)
  fmt.Println("nginx addr:",appConfig.nginxAddr)
  time.Sleep(5* time.Second)
 }
}
func main() {
 conf,err := config.NewConfig("/Users/zhaofan/go_project/src/go_dev/13/config_test/config.conf")
 if err != nil{
  fmt.Println("parse config failed,err:",err)
  return
 }
 //打開(kāi)文件獲取內(nèi)容后,將自己加入到被通知的切片中
 conf.AddNotifyer(appConfigMgr)
 var appConfig = AppConfig{}
 appConfig.port,err = conf.GetInt("server_port")
 if err != nil{
  fmt.Println("get port failed,err:",err)
  return
 }
 fmt.Println("port:",appConfig.port)
 appConfig.nginxAddr,err = conf.GetString("nginx_addr")
 if err != nil{
  fmt.Println("get nginx addr failed,err:",err)
  return
 }
 fmt.Println("nginx addr:",appConfig.nginxAddr)
 appConfigMgr.config.Store(appConfig)
 run()
}

上面代碼中有一段代碼非常重要:

func(a *AppconfigMgr)Callback(conf *config.Config){
 var appConfig = AppConfig{}
 port,err := conf.GetInt("server_port")
 if err != nil{
  fmt.Println("get port failed,err:",err)
  return
 }
 appConfig.port = port
 fmt.Println("port:",appConfig.port)
 nginxAddr,err := conf.GetString("nginx_addr")
 if err != nil{
  fmt.Println("get nginx addr failed,err:",err)
  return
 }
 appConfig.nginxAddr = nginxAddr
 fmt.Println("nginx addr :",appConfig.nginxAddr)
 appConfigMgr.config.Store(appConfig)
}

這里我們實(shí)現(xiàn)了Callback方法,同時(shí)就實(shí)現(xiàn)了我們?cè)赾onfig包中定義的那個(gè)接口

測(cè)試效果如下,當(dāng)我們更改配置文件后,程序中的配置文件也被重新加載

完整的測(cè)試代碼地址:https://github.com/pythonsite/go_simple_code/tree/master/config_test

總結(jié)

以上所述是小編給大家介紹的使用Go語(yǔ)言實(shí)現(xiàn)配置文件熱加載功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!

您可能感興趣的文章:
  • MongoDB 3.4配置文件避免入坑的注意事項(xiàng)
  • MongoDB的安裝及配置文件選項(xiàng)全解
  • 在Django中同時(shí)使用多個(gè)配置文件的方法
  • python用ConfigObj讀寫(xiě)配置文件的實(shí)現(xiàn)代碼

標(biāo)簽:泰安 阿壩 昭通 駐馬店 東營(yíng) 滄州 瀘州 晉中

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《使用Go語(yǔ)言實(shí)現(xiàn)配置文件熱加載功能》,本文關(guān)鍵詞  使用,語(yǔ)言,實(shí)現(xiàn),配置文件,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《使用Go語(yǔ)言實(shí)現(xiàn)配置文件熱加載功能》相關(guān)的同類(lèi)信息!
  • 本頁(yè)收集關(guān)于使用Go語(yǔ)言實(shí)現(xiàn)配置文件熱加載功能的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    青冈县| 全椒县| 大安市| 虹口区| 罗甸县| 苏尼特右旗| 吉木乃县| 含山县| 通榆县| 罗山县| 福清市| 五河县| 高州市| 镶黄旗| 榆中县| 通榆县| 垦利县| 剑阁县| 买车| 攀枝花市| 台中县| 云浮市| 静海县| 巴里| 吉首市| 海宁市| 博乐市| 邢台市| 错那县| 玉屏| 邹平县| 三门峡市| 石楼县| 乌鲁木齐市| 天全县| 彭水| 冷水江市| 敦煌市| 田阳县| 曲阳县| 建阳市|