配置中心

配置中心是集中管理配置信息的组件,优雅的解决了配置的动态变更、权限管理、持久化、运维成本等问题。

1

1. 创建配置

  1. 下载etcd GUI客户端:https://tzfun.github.io/etcd-workbench/
  2. 创建租约
  3. 创建基于租约的key-value存储,将配置写入,并记录key

2. 实现配置中心

func PullConfig() Config {
	ss := subscriber.MustNewEtcdSubscriber(subscriber.EtcdConf{
		Hosts: []string{"localhost:2379"}, // etcd 地址
		Key:   "user-api-test",            // 配置key
	})
	// 创建 configurator
	cc := configurator.MustNewConfigCenter[Config](configurator.Config{
		Type: "yaml", // 配置值类型:json,yaml,toml
	}, ss)

	// 获取配置
	// 注意: 配置如果发生变更,调用的结果永远获取到最新的配置
	v, err := cc.GetConfig()
	if err != nil {
		panic(err)
	}
	cc.AddListener(func() {
		v, err := cc.GetConfig()
		if err != nil {
			panic(err)
		}
		//这个地方要写 触发配置变化后 需要处理的操作
		println("config changed:", v.Name)
	})
	// 如果想监听配置变化,可以添加 listener
	return v
}
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

3. 自定义配置中间件

go-zero官方支持etcd,如果不想使用etcd,比如想要使用consul,我们可以如下做:

# go get github.com/hashicorp/consul/api
# go get github.com/spf13/viper
1
2

func PullConsulConfig() Config {
	ss := NewConsulSubscriber("http://localhost:8500", "config/test/user-api")
	// 创建 configurator
	cc := configurator.MustNewConfigCenter[Config](configurator.Config{
		Type: "yaml", // 配置值类型:json,yaml,toml
	}, ss)

	// 获取配置
	// 注意: 配置如果发生变更,调用的结果永远获取到最新的配置
	v, err := cc.GetConfig()
	if err != nil {
		panic(err)
	}
	cc.AddListener(func() {
		v, err := cc.GetConfig()
		if err != nil {
			panic(err)
		}
		//这个地方要写 触发配置变化后 需要处理的操作
		println("config changed:", v.Name)
	})
	// 如果想监听配置变化,可以添加 listener
	return v
}

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
package config

import (
	"bytes"
	"encoding/json"
	consulApi "github.com/hashicorp/consul/api"
	"github.com/spf13/viper"
	"github.com/zeromicro/go-zero/core/configcenter/subscriber"
	"sync"
)

type ConsulSubscriber struct {
	listeners []func()
	lock      sync.Mutex
	consulCli *consulApi.Client
	Path      string
}

func NewConsulSubscriber(address, path string) subscriber.Subscriber {
	config := &consulApi.Config{
		Address: address,
	}
	client, err := consulApi.NewClient(config)
	if err != nil {
		panic(err)
	}
	return &ConsulSubscriber{
		consulCli: client,
		Path:      path,
	}
}
func (s *ConsulSubscriber) AddListener(listener func()) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.listeners = append(s.listeners, listener)
	return nil
}

func (s *ConsulSubscriber) Value() (string, error) {
	defaultConfig := viper.New()
	defaultConfig.SetConfigType("yaml")
	kvPair, _, err := s.consulCli.KV().Get(s.Path, nil)
	if err != nil {
		panic(err)
	}
	err = defaultConfig.ReadConfig(bytes.NewBuffer(kvPair.Value))
	if err != nil {
		panic(err)
	}
	settings := defaultConfig.AllSettings()
	marshal, _ := json.Marshal(settings)
	return string(marshal), nil
}

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
  consul:
    image: hashicorp/consul:1.18
    ports:
      - "8500:8500"
    volumes:
      - ./volume/consul/data:/consul/data
      - ./volume/consul/consul.d/:/consul/config:rw
    command: >
      consul agent -server
      -bootstrap-expect=1
      -ui
      -client=0.0.0.0
      -data-dir=/consul/data
      --config-dir=/consul/config
1
2
3
4
5
6
7
8
9
10
11
12
13
14