Go語言實(shí)現(xiàn)配置熱加載的方法分享
概述
web項(xiàng)目,經(jīng)常需要熱啟動(dòng)各種各樣的配置信息,一旦這些服務(wù)發(fā)生變更,我們需要重新啟動(dòng)web server,以使配置生效,實(shí)現(xiàn)配置熱加載。這里有幾種方法實(shí)現(xiàn)這個(gè)需求。
go 定時(shí)器協(xié)程實(shí)現(xiàn)
項(xiàng)目結(jié)構(gòu)
首先來看一下整個(gè)項(xiàng)目的目錄結(jié)構(gòu):
- dyconfig // 項(xiàng)目地址
- config // 配置文件目錄
- api.yaml // 采用yaml格式文件
- global // 代碼文件夾
- config
- config_define
- init
- reload
- go.mod // go package管理依賴的包文件
- go.sum // go package管理打包產(chǎn)生的文件
- main.go // web server的入口,主函數(shù)
代碼細(xì)節(jié)
接下來依次看一下各文件的主體內(nèi)容:
conf/api.yaml文件定義了配置項(xiàng),包含server的host及port信息。
service:
server:
env: dev
host: 127.0.0.1
port: 9902global/init.go
package global
import (
"context"
"path"
)
type Config struct {
Conf struct {
FilePath string
LastModifyTime int64
}
ctx context.Context
cancel context.CancelFunc
}
func NewConfig() (*Config, error) {
conf := new(Config)
conf.ctx, conf.cancel = context.WithCancel(context.Background())
conf.Conf.FilePath = path.Join("./config", "api.yaml")
APIconfig = conf.loadRoute()
go conf.reload() //開啟協(xié)程監(jiān)聽routeNotify
go func() {
for {
select {
case lastModifyTime, ok := <-routeNotify:
if !ok {
return
}
conf.Conf.LastModifyTime = lastModifyTime
route := routeAtomic.Load().(*APIConf)
if route != nil {
APIconfig = route
}
}
}
}()
return conf, nil
}
func (c *Config) Stop() {
c.cancel()
}定義Config 根據(jù)LastModifyTime 判斷是否有發(fā)生變化,F(xiàn)ilePath為文件路徑
go conf.reload()
開啟協(xié)程監(jiān)聽routeNotify,routeNotify內(nèi)容是文件修改時(shí)間的timestamp
global/reload.go
package global
import (
"fmt"
"gopkg.in/yaml.v3"
"io/ioutil"
"os"
"sync/atomic"
"time"
)
const (
CheckInterval = 5 * time.Second
)
var (
routeAtomic atomic.Value //原子性,解決并發(fā)問題
routeNotify = make(chan int64) //channel 放入timestamp
)
func (c *Config) reload() {
ticker := time.NewTicker(CheckInterval)
defer ticker.Stop()
for {
select {
case <-c.ctx.Done():
close(routeNotify)
return
case <-ticker.C:
if f, err := os.Stat(c.Route.FilePath); err != nil {
fmt.Println(err)
} else if f.ModTime().Unix() != c.Route.LastModifyTime {
if c.Route.LastModifyTime == 0 {
c.Route.LastModifyTime = f.ModTime().Unix()
} else {
routeAtomic.Store(c.loadConfig())
routeNotify <- f.ModTime().Unix()
fmt.Println("配置文件發(fā)生變化")
}
}
}
}
}
func (c *Config) loadConfig() *APIConf {
if fp, err := ioutil.ReadFile(c.Route.FilePath); err != nil {
fmt.Println(err)
return nil
} else {
route := new(APIConf)
if err := yaml.Unmarshal(fp, &route); err != nil {
return nil
}
return route
}
}定時(shí)器監(jiān)聽文件的修改時(shí)間與LastModifyTime是否相同,如果不同,則
package global var ( APIconfig = new(APIConf) )
package global
type ServiceConf struct {
Server struct {
Env string `yaml:"env"`
Host string `yaml:"host"`
Port string `yaml:"port"`
} `yaml:"server"`
}
type APIConf struct {
Service ServiceConf `yaml:"service"`
}mian
package main
import (
"dyconfig/global"
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
if conf, err := global.NewConfig(); err != nil { // 初始化配置
fmt.Println(err)
} else {
defer conf.Stop()
}
gin.SetMode(gin.DebugMode)
r := gin.Default()
r.GET("/ping", func(context *gin.Context) {
fmt.Println("當(dāng)前host是: ", global.APIconfig.Service.Server.Host)
fmt.Println("當(dāng)前port是: ", global.APIconfig.Service.Server.Port)
context.JSON(
200, gin.H{
"host": global.APIconfig.Service.Server.Host,
"port": &global.APIconfig.Service.Server.Port,
})
})
port := global.APIconfig.Service.Server.Port
fmt.Println("當(dāng)前host是: ", global.APIconfig.Service.Server.Host)
fmt.Println("當(dāng)前port是: ", global.APIconfig.Service.Server.Port)
port = ":" + port
_ = r.Run(port)
}調(diào)用示例
1.第一次調(diào)用,port為9902


2. 修改config ,port為9903



到此這篇關(guān)于Go語言實(shí)現(xiàn)配置熱加載的方法分享的文章就介紹到這了,更多相關(guān)Go語言配置熱加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
正則表達(dá)式詳解以及Golang中的應(yīng)用示例
這篇文章主要介紹了正則表達(dá)式詳解以及Golang中應(yīng)用的相關(guān)資料,Go語言的regexp包是Go語言標(biāo)準(zhǔn)庫中的一個(gè)重要組件,用于處理正則表達(dá)式,文中通過代碼將用法介紹的非常詳細(xì),需要的朋友可以參考下2025-07-07
golang API請(qǐng)求隊(duì)列的實(shí)現(xiàn)
本文主要介紹了golang API請(qǐng)求隊(duì)列的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
golang結(jié)構(gòu)體指針的實(shí)現(xiàn)
這篇文章主要介紹了golang結(jié)構(gòu)體指針的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-06-06
grom設(shè)置全局日志實(shí)現(xiàn)執(zhí)行并打印sql語句
本文主要介紹了grom設(shè)置全局日志實(shí)現(xiàn)執(zhí)行并打印sql語句,包括設(shè)置日志級(jí)別、實(shí)現(xiàn)自定義Logger接口以及如何使用GORM的默認(rèn)logger,通過這些方法,可以更好地控制和記錄數(shù)據(jù)庫操作的日志信息2025-03-03
Go返回int64類型字段超出javascript Number范圍的解決方法
這篇文章主要介紹了Go返回int64類型字段超出javascript Number范圍的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
golang優(yōu)化目錄遍歷的實(shí)現(xiàn)方法
對(duì)于go1.16的新變化,大家印象最深的可能是io包的大規(guī)模重構(gòu),但這個(gè)重構(gòu)實(shí)際上還引進(jìn)了一個(gè)優(yōu)化,這篇文章要說的就是這個(gè)優(yōu)化,所以本將給大家介紹golang是如何優(yōu)化目錄遍歷的,需要的朋友可以參考下2024-08-08

