蝰蛇动态加载配置文件有数据赛跑

我想动态加载配置文件,而不是重新启动我的Go应用程序。我写了下面的文件,这些文件可以运行,但有数据竞争。


配置.go


package main


import (

    "github.com/fsnotify/fsnotify"

    "github.com/spf13/viper"

    "log"

    "sync"

    "time"

)


var (

    reloadConfig  = make(chan string)

    reloadConfig2 = make(chan string)

    viperLock1    sync.Mutex

    viperLock2    sync.Mutex

)


func setUpConfig(file string, merge bool, v *viper.Viper) {

    v.AddConfigPath("./")

    v.SetConfigName(file)

    v.SetConfigType("yml")

    if merge {

        err1 := v.MergeInConfig()

        checkForFatalError("fatal error occurred while reading config file!", err1)

    } else {

        err := v.ReadInConfig()

        checkForFatalError("fatal error occurred while reading config file!", err)

    }

    log.Println("Initial config value: ", v.GetString("env"))

}


func loadConfigDynamically(configChannel chan string, viperLock *sync.Mutex, vipe *viper.Viper) {

    viperLock.Lock()

    vipe.OnConfigChange(func(e fsnotify.Event) {

        viperLock.Lock()

        log.Println("config file changed", e.Name)

        environment := vipe.GetString("env")

        configChannel <- environment

        viperLock.Unlock()

    })

    viperLock.Unlock()

    vipe.WatchConfig()

}


func loadMultipleConfigsDynamically() {

    go func() {

        time.Sleep(time.Millisecond * 50)

        vipe2 := viper.New()

        setUpConfig("config_base", false, vipe2)

        loadConfigDynamically(reloadConfig2, &viperLock2, vipe2)


        time.Sleep(time.Millisecond * 50)

        vipe1 := viper.New()

        setUpConfig("config", false, vipe1)

        loadConfigDynamically(reloadConfig, &viperLock1, vipe1)

    }()

}


主要.go


package main


import (

    log "github.com/sirupsen/logrus"

    "os"

    "os/signal"

    "syscall"

)


var reloadConfigNow = make(chan bool)

var reloadConfigAgain = make(chan bool)

var newConfigValue string


func main() {

    loadMultipleConfigsDynamically()

    go printUpdatedValueOnly()

    go justAnotherGoroutine()

    go yetAnotherGoroutine()

    shutdownAppGracefully()

}



SMILET
浏览 78回答 2
2回答

潇湘沐

您锁定调用并设置了一个功能,它也锁定了。viperLockvipe.WatchConfig()vipe.OnConfigChangeviperLock因为你已经打过电话,然后它开始打电话给单独的go例程。它也试图获取相同的锁。这就是存在争用条件的原因。vipe.WatchConfig()vipe.OnConfigChange在设置后和释放锁后调用。vipe.WatchConfig()vipe.OnConfigChange应按如下方式更正。func loadConfigDynamically() {&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second)&nbsp; &nbsp; &nbsp; &nbsp; viperLock.Lock()&nbsp; &nbsp; &nbsp; &nbsp; vipe.OnConfigChange(func(e fsnotify.Event) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; viperLock.Lock()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println("config file changed", e.Name)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; environment := vipe.GetString("env")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reloadConfig <- environment&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; viperLock.Unlock()&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; viperLock.Unlock()&nbsp; &nbsp; &nbsp; &nbsp; vipe.WatchConfig() //this starting call vipe.OnConfigChange&nbsp; &nbsp; }()}

牧羊人nacy

可能是 go 认为变量正在同时被两个 goroutine 修改和访问,并且修改和访问的位置没有锁定。类似于以下示例:package mainimport (&nbsp; &nbsp; "time")type Foo struct {&nbsp; &nbsp; f func(string)}func (f *Foo) Watch() {&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second * 2)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if f.f != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f.f("hello world")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }()}func (f *Foo) SetF(fun func(string)) {&nbsp; &nbsp; f.f = fun}func main() {&nbsp; &nbsp; f := Foo{}&nbsp; &nbsp; f.Watch()&nbsp; &nbsp; f.SetF(func(s string) {&nbsp; &nbsp; })&nbsp; &nbsp; time.Sleep(time.Second * 5)}它有一场数据竞赛。如果我在修改和读取的地方都放了相同的锁,就不会有数据竞争:package mainimport (&nbsp; &nbsp; "sync"&nbsp; &nbsp; "time")var lock sync.Mutextype Foo struct {&nbsp; &nbsp; f func(string)}func (f *Foo) Watch() {&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second * 2)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lock.Lock() // read places&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if f.f != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f.f("hello world")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lock.Unlock()&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }()}func (f *Foo) SetF(fun func(string)) {&nbsp; &nbsp; f.f = fun}func main() {&nbsp; &nbsp; f := Foo{}&nbsp; &nbsp; f.Watch()&nbsp; &nbsp; lock.Lock() // write places&nbsp; &nbsp; f.SetF(func(s string) {&nbsp; &nbsp; })&nbsp; &nbsp; lock.Unlock()&nbsp; &nbsp; time.Sleep(time.Second * 5)}或者消除两个哥律同时阅读和写入的可能性会很好:func main() {&nbsp; &nbsp; f := Foo{}&nbsp; &nbsp; f.SetF(func(s string) {&nbsp; &nbsp; })&nbsp; &nbsp; f.Watch()&nbsp; &nbsp; time.Sleep(time.Second * 5)}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go