Go 中整合 etcd 实现配置中心
在 go-Gin 框架中整合 etcd 配置中心,并实现配置热加载,通常需要以下几个步骤:
- 连接到 etcd:使用 etcd 的客户端库连接到 etcd 服务器。
- 监听配置变化:在 etcd 中监听配置的键(key)变化。
- 更新配置:当检测到配置变化时,更新应用的配置。
- 重启或重新加载服务:根据配置的变化,可能需要重启服务或重新加载特定的组件。
下面是详细步骤:
安装相关依赖
etcd客户端
1
| go get github.com/coreos/etcd/clientv3
|
gin
1
| go get github.com/gin-gonic/gin
|
全局配置
编写好全局变量,包括 etcd 客户端、要监听的配置的 key以及在项目中使用的全局的 config 配置对象
1 2 3 4
| var etcdClient *clientv3.Client var appConfigKey = "name" var GlobalConfig map[string]interface{}
|
创建 etcd 客户端
1 2 3 4 5 6 7 8 9 10 11
| func InitEtcdClient() { var err error etcdClient, err = clientv3.New(clientv3.Config{ Endpoints: []string{"127.0.0.1:2379"}, DialTimeout: 5 * time.Second, }) if err != nil { goLog.Fatalf("etcd client init err: %v", err) } }
|
创建监听函数
创建 etcd 监听函数,实时监听配置变化
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
| func WatchConfig() { getResp, err := etcdClient.Get(context.Background(), appConfigKey) if err != nil { goLog.Fatalf("Error getting config from etcd: %v", err) } if len(getResp.Kvs) > 0 { fmt.Println(getResp.Kvs[0].Value) err = json.Unmarshal(getResp.Kvs[0].Value, &GlobalConfig) if err != nil { goLog.Fatalf("Error unmarshalling config: %v", err) } goLog.Printf("Config first load success") } else { goLog.Printf("Config not found in etcd") }
rch := etcdClient.Watch(context.Background(), appConfigKey, clientv3.WithPrevKV()) for wresp := range rch { for _, ev := range wresp.Events { if ev.Type == clientv3.EventTypePut { err := json.Unmarshal(ev.Kv.Value, &GlobalConfig) if err != nil { goLog.Printf("Error unmarshal config update: %v", err) continue } goLog.Printf("Updated config successfully") } } } }
|
注意:这里是默认存储在 etcd 中的配置文件信息为 json 格式,若使用的是其他类型的格式,可以使用对应的解析库,如 yml 的 yaml.Unmarshal 等
启动 Web 服务和 etcd 客户端协程
在一个单独的协程中处理配置的热更新,如果需要主动更新配置,可以新增一个更新配置的http接口,方便调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| func main() { initEtcdClient() go watchConfig()
r := gin.Default() r.GET("/reload", func(c *gin.Context) { c.JSON(200, gin.H{"message": "Config reloaded"}) })
r.Run(":8080") }
|
若其他中间件依赖配置中心的配置信息,必须要保证 etcd 客户端首先执行,可以放在 init 函数中,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func init() { conf.InitEtcdClient() go conf.WatchConfig() if err := db.Init(); err != nil { panic(fmt.Sprintf("db init fail: %+v", err)) } if err := redis.SetUpRedisDb(); err != nil { panic(fmt.Sprintf("redis init fail: %+v", err)) } }
|