介紹
Etcd 是一個 golang 編寫的分布式、高可用的一致性鍵值存儲系統,用于配置共享和服 務發現等。它使用 Raft 一致性算法來保持集群數據的一致性,且客戶端通過長連接 watch 功能,能夠及時收到數據變化通知,相較于 Zookeeper 框架更加輕量化。以下 是關于 etcd 的安裝與使用方法的詳細介紹。
安裝
Etcd 首先,需要在你的系統中安裝 Etcd。Etcd 是一個分布式鍵值存儲,通常用于服務發現 和配置管理。以下是在 Linux 系統上安裝 Etcd 的基本步驟:
1. 安裝 Etcd:
sudo apt-get install etcd
1. 啟動 Etcd 服務:
sudo systemctl start etcd
1. 設置 Etcd 開機自啟:
sudo systemctl enable etcd
節點配置
如果是單節點集群其實就可以不用進行配置,默認 etcd 的集群節點通信端口為 2380, 客戶端訪問端口為 2379.
若需要修改,則可以配置:/etc/default/etcd
?#節點名稱,默認為 "default"
ETCD_NAME="etcd1"
#數據目錄,默認為 "${name}.etcd"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
#用于客戶端連接的 URL。 ETCD_LISTEN_CLIENT_URLS="http://192.168.65.132:2379,http://127.0.0 .1:2379"
#用于客戶端訪問的公開,也就是提供服務的 URL ETCD_ADVERTISE_CLIENT_URLS="http://192.168.65.132:2379,http://127. 0.0.1:2379"
#用于集群節點間通信的 URL。 ETCD_LISTEN_PEER_URLS="http://192.168.65.132:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.65.132:2380"
#心跳間隔時間-毫秒
ETCD_HEARTBEAT_INTERVAL=100
#選舉超時時間-毫秒
ETCD_ELECTION_TIMEOUT=1000
#以下為集群配置,若無集群則需要注銷
#初始集群狀態和配置--集群中所有節點 #ETCD_INITIAL_CLUSTER="etcd1=http://192.168.65.132:2380,etcd2=http ://192.168.65.132:2381,etcd3=http://192.168.65.132:2382"
#初始集群令牌-集群的 ID
#ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #ETCD_INITIAL_CLUSTER_STATE="new"
#以下為安全配置,如果要求 SSL 連接 etcd 的話,把下面的配置啟用,并修改文件路徑 #ETCD_CERT_FILE="/etc/ssl/client.pem"
#ETCD_KEY_FILE="/etc/ssl/client-key.pem"
#ETCD_CLIENT_CERT_AUTH="true"
#ETCD_TRUSTED_CA_FILE="/etc/ssl/ca.pem"
#ETCD_AUTO_TLS="true"
#ETCD_PEER_CERT_FILE="/etc/ssl/member.pem" #ETCD_PEER_KEY_FILE="/etc/ssl/member-key.pem" #ETCD_PEER_CLIENT_CERT_AUTH="false" #ETCD_PEER_TRUSTED_CA_FILE="/etc/ssl/ca.pem" #ETCD_PEER_AUTO_TLS="true"
單節點運行示例
etcd --name etcd1 --initial-advertise-peer-urls
http://192.168.65.132:2380 \
--listen-peer-urls http://192.168.65.132:2380 \
--listen-client-urls http://192.168.65.132:2379 \
--advertise-client-urls http://192.168.65.132:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster
etcd1=http://192.168.65.132:2380,etcd2=http://192.168.65.132:2381, etcd3=http://192.168.65.132:2382 \
--initial-cluster-state new &> nohup1.out &
運行驗證
etcdctl put mykey "this is awesome"
如果出現報錯:
etcdctl put mykey "this is awesome" No help topic for 'put'
則 sudo vi /etc/profile 在末尾聲明環境變量 ETCDCTL_API=3 以確定 etcd 版本。
Bash export ETCDCTL_API=3 完畢后,加載配置文件,并重新執行測試指令
source /etc/profile
etcdctl put mykey "this is awesome" OK
etcdctl get mykey mykey this is awesome
etcdctl del mykey
搭建服務注冊發現中心
使用 Etcd 作為服務注冊發現中心,你需要定義服務的注冊和發現邏輯。這通常涉及到 以下幾個操作:
- 服務注冊:服務啟動時,向 Etcd 注冊自己的地址和端口。
- 服務發現:客戶端通過 Etcd 獲取服務的地址和端口,用于遠程調用。
- 健康檢查:服務定期向 Etcd 發送心跳,以維持其注冊信息的有效性。
etcd 采用 golang 編寫,v3 版本通信采用 grpc API,即(HTTP2+protobuf); 官方只維護了 go 語言版本的 client 庫, 因此需要找到 C/C++ 非官方的 client 開發庫:
etcd-cpp-apiv3
etcd-cpp-apiv3 是一個 etcd 的 C++版本客戶端 API。它依賴于 mipsasm, boost, protobuf, gRPC, cpprestsdk 等庫。
etcd-cpp-apiv3 的 GitHub 地址是:https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3
依賴安裝:
sudo apt-get install libboost-all-dev libssl-dev
sudo apt-get install libprotobuf-dev protobuf-compiler-grpc
sudo apt-get install libgrpc-dev libgrpc++-dev
sudo apt-get install libcpprest-dev
api 框架安裝
git clone https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.git cd etcd-cpp-apiv3
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr
make -j$(nproc) && sudo make install
客戶端類與接口介紹:
//pplx::task 并行庫異步結果對象?
//阻塞方式 get(): 阻塞直到任務執行完成,并獲取任務結果?
//非阻塞方式 wait(): 等待任務到達終止狀態,然后返回任務狀態?namespace etcd {
?? ?class Value {
?? ??? ?bool is_dir();//判斷是否是一個目錄?
?? ??? ??? ?std::string const& key() //鍵值對的 key 值?
?? ??? ??? ?std::string const& as_string()//鍵值對的 val 值??? ??? ??? ?int64_t lease() //用于創建租約的響應中,返回租約 ID?
?? ?}
?? ?//etcd 會監控所管理的數據的變化,一旦數據產生變化會通知客戶端?
?? ?//在通知客戶端的時候,會返回改變前的數據和改變后的數據?
?? ?class Event {
?? ??? ?enum class EventType {
?? ??? ??? ?PUT, //鍵值對新增或數據發生改變?
?? ??? ??? ?DELETE_,//鍵值對被刪除?
?? ??? ??? ?INVALID,
?? ??? ?};
?? ??? ?enum EventType event_type()
?? ??? ??? ?const Value& kv()
?? ??? ??? ?const Value& prev_kv()
?? ?}
?? ?class Response {
?? ??? ?bool is_ok()
?? ??? ??? ?std::string const& error_message()
?? ??? ??? ?Value const& value()//當前的數值 或者 一個請求的處理結果?
?? ??? ??? ?Value const& prev_value()//之前的數值?
?? ??? ??? ?Value const& value(int index)//?
?? ??? ??? ?std::vector<Event> const& events();//觸發的事件?
?? ?}?? ?class KeepAlive {
?? ??? ?KeepAlive(Client const& client, int ttl, int64_t lease_id =
?? ??? ??? ?0);
?? ??? ?//返回租約 ID?
?? ??? ?int64_t Lease();
?? ??? ?//停止保活動作?
?? ??? ?void Cancel();
?? ?}?? ?class Client {
?? ??? ?// etcd_url: "http://127.0.0.1:2379"?
?? ??? ?Client(std::string const& etcd_url,
?? ??? ??? ?std::string const& load_balancer = "round_robin");
?? ??? ?//Put a new key-value pair 新增一個鍵值對?
?? ??? ?pplx::task<Response> put(std::string const& key,
?? ??? ??? ?std::string const& value);
?? ??? ?//新增帶有租約的鍵值對 (一定時間后,如果沒有續租,數據自動刪除)?
?? ??? ?pplx::task<Response> put(std::string const& key,
?? ??? ??? ?std::string const& value,
?? ??? ??? ?const int64_t leaseId);
?? ??? ?//獲取一個指定 key 目錄下的數據列表?
?? ??? ?pplx::task<Response> ls(std::string const& key);
?? ??? ?//創建并獲取一個存活 ttl 時間的租約?
?? ??? ?pplx::task<Response> leasegrant(int ttl);
?? ??? ?//獲取一個租約保活對象,其參數 ttl 表示租約有效時間?
?? ??? ?pplx::task<std::shared_ptr<KeepAlive>> leasekeepalive(int
?? ??? ??? ?ttl);
?? ??? ?//撤銷一個指定的租約?
?? ??? ?pplx::task<Response> leaserevoke(int64_t lease_id);
?? ??? ?//數據鎖?
?? ??? ?pplx::task<Response> lock(std::string const& key);
?? ?}?? ?class Watcher {
?? ??? ?Watcher(Client const& client,
?? ??? ??? ?std::string const& key, //要監控的鍵值對 key?
?? ??? ??? ?std::function<void(Response)> callback, //發生改變后的回調?
?? ??? ??? ?bool recursive = false); //是否遞歸監控目錄下的所有數據改變?
?? ??? ?Watcher(std::string const& address,
?? ??? ??? ?std::string const& key,
?? ??? ??? ?std::function<void(Response)> callback,
?? ??? ??? ?bool recursive = false);
?? ??? ?//阻塞等待,直到監控任務被停止?
?? ??? ?bool Wait();
?? ??? ?bool Cancel();
?? ?}
?? ?//........
}
?封裝服務發現與注冊功能:
在服務的注冊與發現中,主要基于 etcd 所提供的可以設置有效時間的鍵值對存儲來實現。
服務注冊
主要是在 etcd 服務器上存儲一個租期 ns 的保活鍵值對,表示所能提供指定服務的節 點主機,比如 /service/user/instance-1 的 key,且對應的 val 為提供服務的主機節點地址:
<key, val>-- < /service/user/instance-1, 127.0.0.1:9000>
- /service 是主目錄,其下會有不同服務的鍵值對存儲
- /user 是服務名稱,表示該鍵值對是一個用戶服務的節點
- /instance-1 是節點實例名稱,提供用戶服務可能會有很多節點,每個節點都應該有自己獨立且唯一的實例名稱
當這個鍵值對注冊之后,服務發現方可以基于目錄進行鍵值對的發現。 且一旦注冊節點退出,保活失敗,則 3s 后租約失效,鍵值對被刪除,etcd 會通知發現 方數據的失效,進而實現服務下線通知的功能。
服務發現
服務發現分為兩個過程:
- 剛啟動客戶端的時候,進行 ls 目錄瀏覽,進行/service 路徑下所有鍵值對的獲取
- 對關心的服務進行 watcher 觀測,一旦數值發生變化(新增/刪除),收到通知進行節點的管理
如果 ls 的路徑為/service,則會獲取到 /service/user, /service/firend, ...等其路徑下的 所有能夠提供服務的實例節點數據。
如果 ls 的路徑為 /service/user, 則會獲取到 /service/user/instancd-1, /service/user/instance-2,....等所有提供用戶服務的實例節點數據。
客戶端可以將發現的所有<實例 - 地址>管理起來,以便于進行節點的管理:
- 收到新增數據通知,則向本地管理添加新增的節點地址 -- 服務上線
- 收到刪除數據通知,則從本地管理刪除對應的節點地址 -- 服務下線
因為管理了所有的能夠提供服務的節點主機的地址,因此當需要進行 rpc 調用的時候, 則根據服務名稱,獲取一個能夠提供服務的主機節點地址進行訪問就可以了,而這里 的獲取策略,我們采用 RR 輪轉策略。
封裝思想:
將 etcd 的操作全部封裝起來,也不需要管理數據,只需要向外四個基礎操作接口:
- 進行服務注冊,也就是向 etcd 添加 服務-主機地址>的數據
- 進行服務發現,獲取當前所有能提供服務的信息
- 設置服務上線的處理回調接口 ? 設置服務下線的處理回調接口
這樣封裝之后,外部的 rpc 調用模塊,可以先獲取所有的當前服務信息,建立通信連接進行 rpc 調用,也能在有新服務上線的時候新增連接,以及下線的時候移除連接。