一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術(shù)及教程分享平臺!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Golang - viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽

viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽

2020-07-09 10:07Go語言中文網(wǎng) Golang

這篇文章主要介紹了viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

viper作為配置框架,其功能非常的強(qiáng)大,我們沒有理由不去了解一下。我們先看官網(wǎng)對它的功能簡介:

viper是完整配置解決方案,他可以處理所有類型和格式的配置文件,他有如下功能:

  • 設(shè)置默認(rèn)配置
  • 支持讀取 JSON TOML YAML HCL 和 Java 屬性配置文件
  • 監(jiān)聽配置文件變化,實時讀取讀取配置文件內(nèi)容
  • 讀取環(huán)境變量值
  • 讀取遠(yuǎn)程配置系統(tǒng) (etcd Consul) 和監(jiān)控配置變化
  • 讀取命令 Flag 值
  • 讀取 buffer 值
  • 讀取確切值

乍一看,未免有相見恨晚之感,可仔細(xì)一想,不免腦袋里有另外一種聲音:不會不支持讀取 zookeeper 吧?好吧,至少我是這樣的。

基于這種想法,當(dāng)然要去立馬嘗試,如下:

?
1
viper.AddRemoteProvider("zookeeper", "xx.xx.xx.xx:2181", "/viper/test")

返回結(jié)果是:

Unsupported Remote Provider Type zookeeper

果不其然,于是追蹤 viper.AddRemoteProvider 的源碼,發(fā)現(xiàn)viper只支持如下幾種

?
1
var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}

如果就此打住,未免有點(diǎn)太可惜,作為偏執(zhí)狂,總想著能否來改造下viper,讓其支持 zookeeper ,于是在issue上找是否有人遇到同樣的問題,還整讓我找到了, 傳送 。但是不完整,且稍微有點(diǎn)bug。所以根據(jù)他的基礎(chǔ)上,我做了些調(diào)整。進(jìn)入正題,我們開始修改viper源碼。說明下,我的viper版本是最新的 1.7.0

修改源碼

1、添加zookeeper.go

添加的位置: github.com/bketelsen/crypt/zookeeper , zookeeper 目錄需要自己創(chuàng)建, github.com/bketelsen/crypt 是viper的依賴包,會自動下載

viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽

文件內(nèi)容:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package zookeeper
 
import (
    "errors"
    "fmt"
    zk "github.com/samuel/go-zookeeper/zk"
    //"github.com/xordataexchange/crypt/backend"
    "github.com/bketelsen/crypt/backend"
    "strings"
    "time"
)
 
type Client struct {
    client *zk.Conn
    waitIndex uint64
}
 
func New(machines []string) (*Client, error) {
    zkclient, _, err := zk.Connect(machines, time.Second)
    if err != nil {
        return nil, err
    }
    return &Client{zkclient, 0}, nil
}
 
func (c *Client) Get(key string) ([]byte, error) {
    resp, _, err := c.client.Get(key)
    if err != nil {
        return nil, err
    }
    return []byte(resp), nil
}
 
func nodeWalk(prefix string, c *Client, vars map[string]string) error {
    l, stat, err := c.client.Children(prefix)
    if err != nil {
        return err
    }
 
    if stat.NumChildren == 0 {
        b, _, err := c.client.Get(prefix)
        if err != nil {
            return err
        }
        vars[prefix] = string(b)
 
    } else {
        for _, key := range l {
            s := prefix + "/" + key
            _, stat, err := c.client.Exists(s)
            if err != nil {
                return err
            }
            if stat.NumChildren == 0 {
                b, _, err := c.client.Get(s)
                if err != nil {
                    return err
                }
                vars[s] = string(b)
            } else {
                nodeWalk(s, c, vars)
            }
        }
    }
    return nil
}
 
func (c *Client) GetValues(key string, keys []string) (map[string]string, error) {
    vars := make(map[string]string)
    for _, v := range keys {
        v = fmt.Sprintf("%s/%s", key, v)
        v = strings.Replace(v, "/*", "", -1)
        _, _, err := c.client.Exists(v)
        if err != nil {
            return vars, err
        }
        if v == "/" {
            v = ""
        }
        err = nodeWalk(v, c, vars)
        if err != nil {
            return vars, err
        }
    }
    return vars, nil
}
 
func (c *Client) List(key string) (backend.KVPairs, error) {
    var list backend.KVPairs
    resp, stat, err := c.client.Children(key)
    if err != nil {
        return nil, err
    }
 
    if stat.NumChildren == 0 {
        return list, nil
    }
 
    entries, err := c.GetValues(key, resp)
    if err != nil {
        return nil, err
    }
 
    for k, v := range entries {
        list = append(list, &backend.KVPair{Key: k, Value: []byte(v)})
    }
    return list, nil
}
 
func (c *Client) createParents(key string) error {
    flags := int32(0)
    acl := zk.WorldACL(zk.PermAll)
 
    if key[0] != '/' {
        return errors.New("Invalid path")
    }
 
    payload := []byte("")
    pathString := ""
    pathNodes := strings.Split(key, "/")
    for i := 1; i < len(pathNodes); i++ {
        pathString += "/" + pathNodes[i]
        _, err := c.client.Create(pathString, payload, flags, acl)
        // not being able to create the node because it exists or not having
        // sufficient rights is not an issue. It is ok for the node to already
        // exist and/or us to only have read rights
        if err != nil && err != zk.ErrNodeExists && err != zk.ErrNoAuth {
            return err
        }
    }
    return nil
}
 
func (c *Client) Set(key string, value []byte) error {
    err := c.createParents(key)
    if err != nil {
        return err
    }
    _, err = c.client.Set(key, []byte(value), -1)
    return err
}
 
func (c *Client) Watch(key string, stop chan bool) <-chan *backend.Response {
    respChan := make(chan *backend.Response, 0)
    go func() {
        for {
            resp, _, watch, err := c.client.GetW(key)
            if err != nil {
                respChan <- &backend.Response{nil, err}
                time.Sleep(time.Second * 5)
            }
 
            select {
            case e := <-watch:
                if e.Type == zk.EventNodeDataChanged {
                    resp, _, err = c.client.Get(key)
                    if err != nil {
                        respChan <- &backend.Response{nil, err}
                    }
                    c.waitIndex = 0
                    respChan <- &backend.Response{[]byte(resp), nil}
                }
            }
        }
    }()
    return respChan
}

這個文件是實現(xiàn) ConfigManager 接口,我們在上圖中看到 etcdconsulfilestore ,均有實現(xiàn)該接口,接口的定義很簡單

?
1
2
3
4
5
6
type ConfigManager interface {
    Get(key string) ([]byte, error)
    List(key string) (KVPairs, error)
    Set(key string, value []byte) error
    Watch(key string, stop chan bool) <-chan *Response
}

2、修改config.go

文件的位置: github.com/bketelsen/crypt/config/config.go ,如下圖

 

viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽

func NewStandardEtcdConfigManager(machines []string) (ConfigManager, error) 方法下面添加如下方法:

?
1
2
3
4
5
6
7
8
9
// NewStandardZookeeperConfigManager returns a new ConfigManager backed by Zookeeper.
// Data will be encrypted.
func NewStandardZookeeperConfigManager(machines []string) (ConfigManager, error) {
    store, err := zookeeper.New(machines)
    if err != nil {
        return nil, err
    }
    return NewStandardConfigManager(store)
}

func NewEtcdConfigManager(machines []string, keystore io.Reader) (ConfigManager, error) 方法下面添加如下方法:

?
1
2
3
4
5
6
7
8
9
// NewZookeeperConfigManager returns a new ConfigManager backed by zookeeper.
// Data will be encrypted.
func NewZookeeperConfigManager(machines []string, keystore io.Reader) (ConfigManager, error) {
    store, err := zookeeper.New(machines)
    if err != nil {
        return nil, err
    }
    return NewConfigManager(store, keystore)
}

這兩個方法是初始化 ConfigManager 對象,也就是我們剛才添加的 zookeeper.go 文件的對象

3、修改remote.go

文件的位置: github.com/spf13/viper/remote/remote.go ,如下圖

 

viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽

找到74行,用下面的代碼替換 func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) 方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
    var cm crypt.ConfigManager
    var err error
 
    if rp.SecretKeyring() != "" {
        var kr *os.File
        kr, err = os.Open(rp.SecretKeyring())
        if err != nil {
            return nil, err
        }
        defer kr.Close()
        switch rp.Provider() {
        case "etcd":
            cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr)
        case "zookeeper":
            cm, err = crypt.NewZookeeperConfigManager([]string{rp.Endpoint()}, kr)
        case "firestore":
            cm, err = crypt.NewFirestoreConfigManager([]string{rp.Endpoint()}, kr)
        default:
            cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr)
        }
    } else {
        switch rp.Provider() {
        case "etcd":
            cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()})
        case "zookeeper":
            cm, err = crypt.NewStandardZookeeperConfigManager([]string{rp.Endpoint()})
        case "firestore":
            cm, err = crypt.NewStandardFirestoreConfigManager([]string{rp.Endpoint()})
        default:
            cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()})
        }
    }
    if err != nil {
        return nil, err
    }
    return cm, nil
}

細(xì)心的讀者可能已經(jīng)發(fā)現(xiàn),其實就添加了兩個case選項:

viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽

4、修改viper.go

文件的位置: github.com/spf13/viper/viper.go ,如下圖

 

viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽

取+監(jiān)聽zookeeper(1)\image-20200521222843002.png)

找到兩個 SupportedRemoteProviders 定義的定法,1.7.0版本的行號分別是:290,331。只要添加 zookeeper ,即可

?
1
SupportedRemoteProviders = []string{"etcd", "consul", "firestore", "zookeeper"}

好了,修改代碼的工作已經(jīng)完了,接下來我們來測試:

測試

注意:zookeeper中已經(jīng)設(shè)置了內(nèi)容

set /viper/test {"appName":"test","nodes":["127.0.0.1","127.0.0.2","127.0.0.3"]}

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main
 
import (
    "fmt"
    "github.com/fsnotify/fsnotify"
    "github.com/spf13/viper"
    _ "github.com/spf13/viper/remote"
    "time"
)
 
 
type config struct {
    AppName string
    Nodes []string
}
 
func main() {
    var waitGroup=sync.WaitGroup{}
    waitGroup.Add(1)
    readRemoteZookeeper()
    go watchRemoteZookeeper()
    waitGroup.Wait()
}
 
func readRemoteZookeeper() {
    viper.AddRemoteProvider("zookeeper", "62.234.15.24:2181", "/viper/test")
    viper.SetConfigType("json")
    err := viper.ReadRemoteConfig()
    if err != nil {
        panic(fmt.Sprintf("read remote zookeeper error:+%v", err))
    }
 
    var C config
    viper.Unmarshal(&C)
    fmt.Printf("從zookeeper讀取配置內(nèi)容:%+v\n", C)
}
 
func watchRemoteZookeeper() {
    go func() {
        for {
  //delay after each request
            time.Sleep(time.Second * 5)
            err := viper.WatchRemoteConfig()
            if err != nil {
                fmt.Errorf("unable to read remote config: %v", err)
                continue
            }
            fmt.Printf("從zookeeper讀取更新內(nèi)容:appName=%s,nodes=%+v\n", viper.Get("appName"), viper.Get("nodes"))
        }
    }()
}

輸出內(nèi)容:

從zookeeper讀取配置內(nèi)容:{AppName:test Nodes:[127.0.0.1 127.0.0.2 127.0.0.3]}
從zookeeper讀取更新內(nèi)容:appName=test,nodes=[127.0.0.1 127.0.0.2 127.0.0.3]

如果我們修改zookeeper的內(nèi)容,則viper會讀取到更新后的內(nèi)容:

?
1
set /viper/test {"appName":"test","nodes":["127.0.0.1","127.0.0.2","127.0.0.3","127.0.0.4"]}
?
1
從zookeeper讀取更新內(nèi)容:appName=test,nodes=[127.0.0.1 127.0.0.2 127.0.0.3 127.0.0.4]

結(jié)語

讓viper支持 zookeeper 并不復(fù)雜的,并且基本上不需要修改原有的方法, 這要?dú)w結(jié)于viper用到一個非常重要的設(shè)計原則: 開閉原則 ,讀者可以自行體會。

關(guān)于viper的基本使用, github 已經(jīng)有非常詳細(xì)的例子,這里就不再贅述,如有疑問,可以私信我

到此這篇關(guān)于viper配置框架的介紹支持zookeeper的讀取和監(jiān)聽的文章就介紹到這了,更多相關(guān)viper配置框架支持zookeeper的讀取和監(jiān)聽內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://studygolang.com/articles/28829

延伸 · 閱讀

精彩推薦
  • Golanggo日志系統(tǒng)logrus顯示文件和行號的操作

    go日志系統(tǒng)logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統(tǒng)logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • Golanggolang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang如何使用struct的tag屬性的詳細(xì)介紹

    golang如何使用struct的tag屬性的詳細(xì)介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細(xì)介紹,從例子說起,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網(wǎng)11352020-05-21
  • Golanggolang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關(guān)于golang的httpserver優(yōu)雅重啟的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,...

    helight2992020-05-14
  • GolangGolang中Bit數(shù)組的實現(xiàn)方式

    Golang中Bit數(shù)組的實現(xiàn)方式

    這篇文章主要介紹了Golang中Bit數(shù)組的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨(dú)尊11682021-06-09
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • GolangGolang通脈之?dāng)?shù)據(jù)類型詳情

    Golang通脈之?dāng)?shù)據(jù)類型詳情

    這篇文章主要介紹了Golang通脈之?dāng)?shù)據(jù)類型,在編程語言中標(biāo)識符就是定義的具有某種意義的詞,比如變量名、常量名、函數(shù)名等等,Go語言中標(biāo)識符允許由...

    4272021-11-24
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
主站蜘蛛池模板: 天天综合色天天综合 | 国产成人咱精品视频免费网站 | 美女的让男人桶爽免费看 | 亚洲精品www久久久久久久软件 | 日本红怡院亚洲红怡院最新 | 吻戏辣妞范1000免费体验 | 美女脱了内裤打开腿让人桶网站o | aa一级护士医生毛片 | 日韩无砖2021特黄 | 32pao强力打造免费高速高清 | 暖暖的免费观看高清视频韩国 | 亚洲国产三级在线观看 | 久见久热 这里只有精品 | 精品欧美一区二区精品久久 | 国产日韩欧美一区 | 草莓丝瓜芭乐樱桃榴莲色多黄 | 娇妻终于接受了3p的调教 | 国产成人综合精品一区 | 国产精亚洲视频 | 蜜桃久久久亚洲精品成人 | free chinese麻豆| 门房秦大爷在线阅读 | 拔插拔插8x8x海外华人免费视频 | 亚洲一区二区三区久久精品 | 欧美影院天天5g天天爽 | 国产麻豆精品视频 | 国产精品久久久久这里只有精品 | 国产成人亚洲综合网站不卡 | 色愉拍亚洲偷自拍 | 国产成人精品实拍在线 | 亚洲精品一区波多野结衣 | 99久久综合给久久精品 | 俄罗斯处女| 久久精品黄AA片一区二区三区 | haodiaocao的视频这里看 | 日韩欧美一区二区三区 | 日本在线亚州精品视频在线 | 成年人免费在线看的惊悚动作片 | 国产精品美女福利视频免费专区 | 久久伊人中文字幕有码 | 黄瓜污视频 |