別再手寫配置了!動態配置管理實踐指南

一、分佈式配置的意義

1.1 什麼是分佈式配置

分佈式配置管理是指在分佈式系統中,對不同節點上的配置文件進行集中式、動態化管理。

與傳統通過修改配置文件進行配置相比,分佈式配置管理系統具有以下優點:

(1) 中心化管理: 配置集中放在配置服務器上, 方便管理和控制。

(2) 動態更新: 支持在線動態修改配置, 服務端會主動推送配置文件更新到客戶端。

(3) 高可用 & 數據一致性: 配置服務器會構建集羣防止單點故障, 並使用 raft 等算法確保配置更新的一致性。

(4) 版本管理: 支持客戶端配置文件的版本管理和歷史記錄追蹤。

(5) 訪問控制: 支持 username 和 password 訪問控制。

(6) 易操作: 簡單易用的 UI 界面及 API 操作方式。

因此,分佈式配置管理在分佈式架構中非常重要,能解決分佈式系統配置管理難題,如服務發現、狀態管理等,是實現彈性擴展的基石。

1.2 分佈式系統中的應用

Go 語言是 Google 開發的一種靜態強類型、編譯型、併發型編程語言。

它在雲原生分佈式系統中得到廣泛應用的主要優點包括:

(1) 併發模型: 基於 CSP 併發模型, GOROUTINE 及 CHANNEL, 可以方便進行高併發控制。

(2) 高效編譯 & 部署: 編譯快速, 無外部依賴, 編譯出的程序只有一個二進制文件, 方便部署。

(3) 內存管理: 基於內存拷貝避免外部指針, GC 高效回收, 適合長期運行的服務。

(4) 網絡支持: 對網絡和 RPC 支持良好, 容易構建分佈式系統。

(5) 雲原生: Go 語言天然支持 Docker 及 Kubernetes 等雲原生技術。

因此,Go 語言非常適合構建大規模分佈式系統, 如微服務、配置管理系統等,分佈式配置管理正是 Go 語言 可以發揮優勢的領域。

二、分佈式配置管理方案

Go 語言常用的開源分佈式配置管理系統主要包括:

2.1 etcd

etcd 是由 CoreOS 開發的一個高可用的分佈式鍵值存儲,常用於服務發現、配置共享和狀態管理。etcd 內置 raft 協議, 實現分佈式一致性和高可用性。

2.2 Consul

Consul 是 HashiCorp 公司推出,Go 語言編寫的開源分佈式高可用系統。內置服務網格解決方案 connect,主要用於服務註冊與發現、健康監測、KV 存儲、多數據中心方案等。

2.3 ZooKeeper

ZooKeeper 是 Apache 軟件基金會推出的一個開源的分佈式協調服務, 用於分佈式環境中的應用程序協調、配置維護和組服務。它基於 Paxos 算法, 支持高可用分佈式一致性。

此外, 還有組件化的配置管理框架 kelseyhightower/envconfig,阿里巴巴推出的分佈式配置中心 Nacos 等。本文重點以 etcd 和 Consul 爲例,介紹 Go 語言操作方式。

三、基於 etcd 的配置管理

3.1 etcd 簡介

etcd 通過 Raft 一致性算法保證高可用, 存儲的數據可完全 SERIALIZABLE(順序一致)。主要功能包括:

1 服務發現, 保存服務實例信息

2 消息發佈 / 訂閱, 支持訂閱關鍵事件

3 分佈式鎖服務, 實現分佈式併發控制

4 集羣成員管理, 節點健康狀態檢測

5 配置中心, 集中存儲配置信息

3.2 使用 etcd 進行配置管理

可通過 PUT、GET 操作保存和獲取配置信息, 並通過 WATCH 機制實時監聽配置變更。

import (
    "context"
    "time"
    "go.etcd.io/etcd/client/v3" 
)
func main() {
   cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
   })
   // put 存儲配置
   _, err = cli.Put(context.TODO(), "/config/rate", "1.5")
   // get 獲取配置 
   resp, err := cli.Get(context.TODO(), "/config/rate")
   // 監聽配置變化
   rch := cli.Watch(context.Background(), "/config/rate")
   for wresp := range rch {
        for _, ev := range wresp.Events {
            fmt.Printf("%s %q : %q\n", ev.Type, ev.Kv.Key, ev.Kv.Value)
        }
   }  
}

3.3 操作 etcd 客戶端示例

以下案例演示 Go 客戶端設置和獲取 etcd 鍵值, 以及 Watch 機制的使用。

package main
import (
    "context"
    "fmt"
    "time"
    "go.etcd.io/etcd/client/v3"
)
func main() {
    cli, err := clientv3.New(clientv3.Config{ 
        Endpoints:   []string{"127.0.0.1:2379"}, 
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        fmt.Println("connect to etcd failed, err:", err)
        return
    }
    fmt.Println("connect to etcd success")
    defer cli.Close()
    // put 存儲配置
    _, err = cli.Put(context.TODO(), "/config/rate", "1.5")
    if err != nil {
        fmt.Printf("put to etcd failed, err:%v\n", err)
        return
    }
    // get 獲取配置
    resp, err := cli.Get(context.TODO(), "/config/rate")
    if err != nil {
        fmt.Printf("get from etcd failed, err:%v\n", err)
        return
    }
    for _, ev := range resp.Kvs {
       fmt.Printf("get from etcd success, key:%s value:%s\n", ev.Key, ev.Value)
    }
    // 監聽配置變化 
    rch := cli.Watch(context.Background(), "/config/rate") 
    for wresp := range rch {
        for _, ev := range wresp.Events {
            fmt.Printf("type: %s key:%s value:%s\n", ev.Type, ev.Kv.Key, ev.Kv.Value) 
        }
    }   
}

四、基於 Consul 的配置管理

4.1 Consul 簡介

Consul 主要功能包括:

1、服務發現與健康監測: 支持基於 DNS 或 HTTP 的服務發現, 節點健康監測。

2、KV 存儲: Key/Value 的存儲方式, 用於組態文件、標記服務元數據。

3、多數據中心: 內置 WAN 集羣可以實現多個 Consul 數據中心。

4、可視化 Web 界面: Web 控制檯直觀展示 Consul 集羣狀況。

4.2 使用 Consul 進行配置管理

Consul 的鍵值對存儲可以用於保存程序配置、服務元數據等, 並通過 interface 綁定配置參數。

import (
    "github.com/hashicorp/consul/api"
)
type config struct {
   Rate float64
}
func main() {
    // 創建consul客戶端
   consulConfig := api.DefaultConfig()
   client, _ := api.NewClient(consulConfig)
    // 註冊配置參數
   go func() {
    for {
       client.KV().Put(&api.KVPair{Key:"config/rate",Value:[]byte("1.5")}, nil)
       time.Sleep(time.Second * 10)
     }
   }()
   // 綁定配置參數 
   var rate config 
   client.KV().Get("config/rate", &rate)
   // 打印配置   
   fmt.Println("Rate:", rate.Rate)
}

五. 集成 etcd 和 Consul

創建客戶端

要連接兩個配置中心, 需要分別創建 etcd 和 Consul 的客戶端:

// 創建etcd客戶端
cli, err := clientv3.New(clientv3.Config{ 
    Endpoints:   []string{"127.0.0.1:2379"},
    DialTimeout: 5 * time.Second,
})
// 創建consul客戶端 
consulCfg := api.DefaultConfig()
consulCfg.Address = "127.0.0.1:8500"
consulClient, err := api.NewClient(consulCfg)

定義配置結構體

根據功能區分配置項, 綁定不同後端:

// 系統核心參數 
type SysConfig struct {
   Addr string // etcd地址  
   MaxPoolSize int // 連接池大小
}
// 服務實例信息
type ServiceInfo struct {  
    ServiceName string
    IPs []string
    Weights []int
}

讀取配置

分別從 etcd 和 Consul 獲取配置並賦值:

// 讀取系統核心配置
var sysConf  SysConfig  
ec.Get("/config/sys", &sysConf) 
// 獲取服務信息
var svcInfo ServiceInfo
cc.Get("/services/my-rpc", &svcInfo)

動態監聽

啓動協程分別 watch 兩個配置中心, 動態更新:

// 監視系統配置
go func() {
    for wresp := range wc.Watch("/config/sys") {
        json.Unmarshal(wresp.Events[0].Kv.Value, &sysConf)      
    }
}()
// 監測服務信息變化 
go func() {
   for resp := range cc.Watch("/services/my-rpc") {
       json.Unmarshal(resp.Value, &svcInfo)  
       refreshCache()
   }
}()

六、分佈式配置管理案例實踐

從典型案例看看 Go 語言如何結合 etcd/Consul 實現配置管理。

6.1 服務發現

利用 Consul 保存服務實例信息, 客戶端通過 Consul DNS 或 HTTP API 獲取服務列表。即使服務擴容或下線, 也始終獲取最新可用的服務。

6.2 動態負載均衡

將服務實例信息和權重保存在 etcd 中, 客戶端 watch 動態變更並重新計算負載均衡選擇。這樣後端服務擴容也無需手動 restart。

6.3 集羣調度

將主從信息寫進 etcd 或 Consul 的 key/value 中, 各節點實時監測 master 存活狀態。一旦主節點下線, 立即從存活節點中選舉新 master, 確保服務高可用。

七、總結

Go 語言分佈式配置管理優點

(1) 生態完整: Go 語言云原生生態夠成熟, 從 etcd/Consul 到 Kubernetes 全面支持。

(2) 性能卓越: 編譯部署高效, 資源消耗低, 可水平擴展到很複雜級別。

(3) 雲原生: 天生適合容器、微服務和服務網格體系。

遇到的問題及解決思路

(1) 鎖爭用: trylock 避免死鎖, 或採用鎖時間段控制。

(2)Watch 阻塞: 設置連接超時, 重試模式避免 Watch 線程阻塞。

(3) 數據一致性: 解析 K-V 時間戳, 強一致性實現順序加鎖。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/AwGW3qDL5EHwnhLW3LvESw