etcd是什么
etcd 是一個分布式鍵值對存儲,設計用來可靠而快速的保存關鍵數據并提供訪問。通過分布式鎖, leader選舉保障可靠的分布式協同。
etcd 特點
-
完全復制,集群中的每個節點均擁有全量數據
-
強一致性,etcd通過raft共識算法保證集群數據的一致性
-
數據結構簡單,僅字符串格式
-
可存儲的數據量較少
-
每個節點支持1000次/秒的寫操作,2000次/秒的讀操作
使用場景
-
服務注冊與服務發現
-
分布式配置中心
-
分布式鎖
-
分布式選主
-
讀多寫少的場景
架構圖
-
Leader:Raft算法中通過競選而產生的處理所有數據提交的節點
-
Follower:競選失敗的節點作為Raft中的從屬節點,為算法提供強一致性保證
-
Candidate:當Follower超過一定時間接收不到Leader的心跳時轉變為Candidate開始競選
-
Learner:etcd集群中的只讀節點,擁有全量數據,不參與Leader的競選
-
BoltDB:一個支持事務的KV存儲引擎,用于對ETCD數據的持久化,支持并發讀事務,串行寫事務
-
WAL 預寫式日志:etcd中的數據提交都會先記錄到日志,日志成功后才會做數據持久化
-
Snapshot,快照:防止WAL日志過多,用于存儲某一時刻etcd的所有數據,可用于節點故障恢復
-
gRPC Server:節點與節點,節點與客戶端通過gRPC 通信和信息同步,每個節點都是一個gRPC 服 務器
-
Raft:共識算法,etcd數據強一致性的保障
-
Client:etcd客戶端,可以是命令行客戶端 etcdctl ,也可以是各個語言版本的client庫,可通過 grpc與http與server端通信
基本原理
MVCC(多版本并發控制)
-
作用
MVCC即在對數據修改時,不是去改變數據的值,而是新增一條數據記錄,數據的版本號遞增1。在讀取 數據時實際上是讀取到當前版本的數據記錄。MVCC旨在提高數據庫的并發性能,更好的解決讀寫之間的 沖突,從而實現讀寫沖突的情況下,不加鎖并發讀。
-
數據版本號:
revision:etcd鍵空間版本號,key發生變更,則revision加1;全局單調遞增,64bits
create_revision: 創建數據時,對應的revision
mod_revision: 數據修改時,對應的revision
version: 表示當前key被修改的次數
分布式鎖
-
鎖的持有者為:前綴為 mymutex 且創建時間最早的key,即根據create version 正序第一個key值
-
etcd中任何數據變化都會導致全局的revision發生遞增
-
客戶端向etcd發送事務,事務內容:1.向etcd添加當前客戶端的鍵值對,并獲得結果;2.向etcd中 獲取目前的鎖持有者。
-
如果不存在鎖持有者,則自身即為持有者;如果鎖持有者的create version 等于當前自身的create version則自身即為持有者
-
如果自身不是持有者,則監聽前綴為 mymutex 且 create version 比自身小一個版本號的key的刪除事件,一旦刪除,則自身獲取到鎖
數據寫流程
-
只有leader節點能處理寫流程,當follower節點收到寫請求后會轉發到leader
-
將請求提案廣播到各個follower節點,并記錄WAL,當大多數節點成功記錄WAL之后,該請求提案 的狀態將變成已提交狀態。
-
由各個節點根據WAL應用數據,對數據進行持久化
-
響應寫請求,返回處理結果
讀流程
線性讀:
-
etcd 默認讀模式是線性讀,在延時和吞吐量上相比串行讀略差一點,適用于對數據一致性要求高的場景。
-
當收到一個線性讀請求時,首先會從 Leader 獲取集群最新的已提交的日志索引。
-
Leader 收到 ReadIndex 請求時,為防止腦裂等異常場景,會向 Follower 節點發送心跳確認,一半 以上節點確認 Leader 身份后才能將已提交的索引 (committed index) 返回給節點。
-
節點則會等待,直到狀態機已應用索引 (applied index) 大于等于 Leader 的已提交索引時 (committed Index),然后去通知讀請求。
串行讀:
節點數據返回、無需通過 Raft 協議與集群進行交互。它具有低延時、高吞吐量的特點,適合對數據 一致性要求不高的場景
ETCD選項說明
-
etcd 集群運行時會同時監聽2個端口,一個是客戶端端口(如:2379)用于給客戶端連接,一個是 集群成員之間(或伙伴,或 peer)通訊的端口(如:2380)。
-
etcd 集群配置的參數名稱有一定規則,一般 --initial 前綴標記用于靜態部署時啟動集群,-- discovery 前綴標記在使用發現服務的方式啟動集群
-
etcd 集群的安全從兩個方面維護,一個是服務端集群成員(peer)之間通信使用 TLS 安全認證, 另一個是客戶端(etcdctl)訪問 etcd 集群使用 TLS 安全認證。
-
TLS 安全認證有一下幾個文件組成
-
CA 證書:ca-file
-
私鑰文件: key-file
-
證書文件: cert-file
-
證書吊銷列表: crl-file
etcdctl命令行
注意事項
1、容器化部署后,需要通過docker exec 在容器中執行命令,使用容器內部的etcdctl命令行客戶端
# 通過docker exec 在容器內部執行命令
docker exec etcd0 /usr/local/bin/etcdctl put key1 value1
2、etcdctl 默認訪問當前節點2379端口,如果當前節點的etcd服務沒有監聽2379端口,那么需要指定- -endpoints,可以指定某一個節點,或者集群所有節點
# etcdctl客戶端訪問監聽了默認端口的節點
docker exec etcd0 /usr/local/bin/etcdctl put key1 value1
# etcdctl客戶端訪問某一個節點
docker exec etcd1 /usr/local/bin/etcdctl put key1 value1 -- endpoints=192.168.239.149:22379
# etcdctl客戶端訪問集群,指定所有節點列表
docker exec etcd1 /usr/local/bin/etcdctl put key1 value1 --endpoints= [192.168.239.149:2379,192.168.239.149:12379,192.168.239.149:22379]
3、容器化部署后,需要與終端保持連接的命令需要啟用docker 虛擬終端交互(指定 -it 選項),例 如:lock、elect、txn等
# 分布式鎖,需要再多個終端執行
docker exec -it etcd0 /usr/local/bin/etcdctl lock mutex1 # 分布式選主,需要在多個終端推選不同的候選人
docker exec -it etcd0 /usr/local/bin/etcdctl elect myelect node1
docker exec -it etcd0 /usr/local/bin/etcdctl elect myelect node2
docker exec -it etcd0 /usr/local/bin/etcdctl elect myelect node3
全局參數
--cacert="" #驗證啟用TLS的安全服務器的證書
--cert="" # TLS證書文件 --command-timeout=5s # 運行命令超時時間
--debug[=false] # 啟用客戶端調試日志記錄
--dial-timeout=2s # 客戶端連接的撥號超時
-d, --discovery-srv="" # 用于查詢描述群集端點的SRV記錄的域名
--discovery-srv-name="" # 使用DNS發現時要查詢的服務名稱
--endpoints=[127.0.0.1:2379] # gRPC 端點
--hex[=false] # 將字節字符串打印為十六進制編碼字符串
--insecure-discovery[=true] # 接受描述群集端點的不安全SRV記錄
--insecure-skip-tls-verify[=false] # 跳過服務器證書驗證(注意:此選項應僅用于測試目的)
--insecure-transport[=true] # 禁用客戶端連接的傳輸安全
--keepalive-time=2s # 客戶端連接的保持生存時間
--keepalive-timeout=6s # 客戶端連接的保持活動超時
--key="" # TLS密鑰文件
--password="" # 密碼
--user="" # 用戶名
-w, --write-out="simple" #設置輸出格式 (fields, json, protobuf, simple, table)
數據操作
put/get/del(數據讀寫)
put #將給定的key寫入到存儲 --ignore-lease[=false] #使用當前租約更新key --ignore-value[=false] #使用當前值更新key --lease="0" # 要附加到key的租約ID(十六進制) --prev-kv[=false] # 返回修改前的上一個鍵值對
del #刪除指定的鍵或鍵范圍 --from-key[=false] # 刪除大于等于給定key的所有key(按byte值比較) --prefix[=false] # 按前綴匹配刪除 --prev-kv[=false] # 刪除后是否返回被刪除的鍵值對
get #獲取給定key或給定范圍的key --consistency="l" # 'l' 代表線性讀(執行raft), 's' 代表串行化讀 --count-only[=false] # 僅獲取計數 --from-key[=false] #使用byte值比較獲取大于或等于給定鍵的鍵,類似*key*,但不等于 --keys-only[=false] # 僅獲取key --limit=0 #最大結果數 --order="" #結果排序,ASCEND 或 DESCEND (默認 ASCEND) --prefix[=false] # 獲取具有匹配前綴的key,key* --print-value-only[=false] #用“simple”輸出格式時僅打印值 --rev=0 #指定kv版本 --sort-by="" # 指定排序字段,CREATE, KEY, MODIFY, VALUE, or VERSION
示例:
docker exec etcd0 /usr/local/bin/etcdctl put key1 value1
docker exec etcd0 /usr/local/bin/etcdctl put key2 value2
docker exec etcd0 /usr/local/bin/etcdctl put key3 value2 # 根據指定key獲取
docker exec etcd0 /usr/local/bin/etcdctl get key1
# 根據前綴獲取 docker
exec etcd0 /usr/local/bin/etcdctl get key --prefix docker exec etcd0 /usr/local/bin/etcdctl put a valuea
docker exec etcd0 /usr/local/bin/etcdctl put b valueb
docker exec etcd0 /usr/local/bin/etcdctl put c valuec # 根據key值byte值比較,獲取大于等于指定key值的所有key
docker exec etcd0 /usr/local/bin/etcdctl get b --from-key
lease(租約)
lease grant # 創建租約
lease keep-alive # 開啟線程,自動續約 --once[=false] # 僅續約一次 lease list #列出所有活動的租約
lease revoke #撤銷租約
lease timetolive # 獲取租約信息 --keys[=false] #獲取附加到此租約的key
示例:
# 創建租約,有效時長20s
docker exec etcd0 /usr/local/bin/etcdctl lease grant 20
# 獲取租約列表
docker exec etcd0 /usr/local/bin/etcdctl lease list
# 根據leaseID 撤銷租約
docker exec etcd0 /usr/local/bin/etcdctl lease revoke 694d869b526b302a
# 查詢指定租約的有效時長信息
docker exec etcd0 /usr/local/bin/etcdctl lease timetolive 694d869b526b30c0
# 開啟線程為指定租約自動續租
docker exec etcd0 /usr/local/bin/etcdctl lease keep-alive 694d869b526b30c2
lock(鎖)
lock # 獲取命名鎖 --ttl=10 #session 超時時間
示例:
# 分布式鎖,需要再多個終端執行
# 終端1
docker exec -it etcd0 /usr/local/bin/etcdctl lock mutex1
# 終端2
docker exec -it etcd0 /usr/local/bin/etcdctl lock mutex1
elect (選舉)
選舉邏輯與鎖邏輯類似,前一個leader卸任之后,后續競選者才能選出新的leader
elect #觀察并參與領導人選舉 -l, --listen[=false] # 觀察模式
txn(事務)
txn # 在一個事務中處理所有請求 -i, --interactive[=false] # 交互模式下的輸入事務
事務API由 if 語句、then語句、else語句組成。基本邏輯:在 If 語句中,可以添加一系列的條件表達式, 若條件表達式全部通過檢查,則執行 Then 語句的 get/put/delete 等操作,否則執行 Else 的 get/put/delete 等操作,支持比較運算符 (>、<、=、!=)
if語句支持項:
-
key 的最近一次修改版本號 mod_revision,簡稱 mod,可以用于檢查 key 最近一次被修改時的版 本號是否符合你的預期。例如:mod("key1") = "111902",檢查key1最近一次修改版本號是否等于 111902
-
key 的創建版本號 create_revision,簡稱 create,可以用于檢測 key 是否已存在。例如: create("key") = "0",來判斷key1是否存在,不存在的話 create_revision 版本號就是0。
-
key 的修改次數 version;可以用于檢查 key 的修改次數是否符合預期。例如:version("key1") > "3",來判斷key1的修改次數是否大于3次
-
key 的值,可以用于檢查 key 的 value 值是否符合預期。例如:value("key1") = "value1",來判斷 key1 的值是否等于 value1
watch(事件監聽)
watch #監聽鍵值或前綴的事件流 -i, --interactive[=false] # 交互式模式 --prefix[=false] # 監聽前綴 --prev-kv[=false] # 獲取事件發生前的上一個鍵值對 --progress-notify[=false] # 從服務器獲取定期監視進度通知 --rev=0 # 從指定版本開始監聽
# 終端1-監聽key2
docker exec etcd0 /usr/local/bin/etcdctl watch key2
# 終端2-對key2做操作
docker exec etcd0 /usr/local/bin/etcdctl put key2 value3
docker exec etcd0 /usr/local/bin/etcdctl put key2 value4
docker exec etcd0 /usr/local/bin/etcdctl del key2
參考連接:https://github.com/0voice