为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
SMILET 2022-09-26 15:49:49
我想动态加载配置文件,而不是重新启动我的Go应用程序。我写了下面的文件,这些文件可以运行,但有数据竞争。配置.gopackage mainimport (    "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)    }()}主要.gopackage mainimport (    log "github.com/sirupsen/logrus"    "os"    "os/signal"    "syscall")var reloadConfigNow = make(chan bool)var reloadConfigAgain = make(chan bool)var newConfigValue stringfunc main() {    loadMultipleConfigsDynamically()    go printUpdatedValueOnly()    go justAnotherGoroutine()    go yetAnotherGoroutine()    shutdownAppGracefully()}
查看完整描述

2 回答

?
潇湘沐

TA贡献1816条经验 获得超6个赞

您锁定调用并设置了一个功能,它也锁定了。viperLockvipe.WatchConfig()vipe.OnConfigChangeviperLock


因为你已经打过电话,然后它开始打电话给单独的go例程。它也试图获取相同的锁。这就是存在争用条件的原因。vipe.WatchConfig()vipe.OnConfigChange


在设置后和释放锁后调用。vipe.WatchConfig()vipe.OnConfigChange


应按如下方式更正。


func loadConfigDynamically() {

    go func() {

        time.Sleep(time.Second)

        viperLock.Lock()

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

            viperLock.Lock()

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

            environment := vipe.GetString("env")

            reloadConfig <- environment

            viperLock.Unlock()

        })

        viperLock.Unlock()

        vipe.WatchConfig() //this starting call vipe.OnConfigChange

    }()

}


查看完整回答
反对 回复 2022-09-26
?
牧羊人nacy

TA贡献1862条经验 获得超7个赞

可能是 go 认为变量正在同时被两个 goroutine 修改和访问,并且修改和访问的位置没有锁定。类似于以下示例:


package main


import (

    "time"

)


type Foo struct {

    f func(string)

}


func (f *Foo) Watch() {

    go func() {

        for {

            time.Sleep(time.Second * 2)

            if f.f != nil {

                f.f("hello world")

            }

        }

    }()

}


func (f *Foo) SetF(fun func(string)) {

    f.f = fun

}


func main() {

    f := Foo{}


    f.Watch()

    f.SetF(func(s string) {

    })


    time.Sleep(time.Second * 5)

}

它有一场数据竞赛。如果我在修改和读取的地方都放了相同的锁,就不会有数据竞争:


package main


import (

    "sync"

    "time"

)


var lock sync.Mutex


type Foo struct {

    f func(string)

}


func (f *Foo) Watch() {

    go func() {

        for {

            time.Sleep(time.Second * 2)

            lock.Lock() // read places

            if f.f != nil {

                f.f("hello world")

            }

            lock.Unlock()

        }

    }()

}


func (f *Foo) SetF(fun func(string)) {

    f.f = fun

}


func main() {

    f := Foo{}


    f.Watch()

    lock.Lock() // write places

    f.SetF(func(s string) {

    })

    lock.Unlock()


    time.Sleep(time.Second * 5)

}

或者消除两个哥律同时阅读和写入的可能性会很好:


func main() {

    f := Foo{}


    f.SetF(func(s string) {

    })

    f.Watch()


    time.Sleep(time.Second * 5)

}


查看完整回答
反对 回复 2022-09-26
  • 2 回答
  • 0 关注
  • 77 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信