etcd鍵值儲存系統的介紹和使用
1、etcd的介紹
etcd是一個高可用的鍵值儲存,用來共享配置和服務發現。etcd是一個分散式減值儲存,提供了一種可靠的方式來將資料儲存在一個機器叢集中,它是開源的,並且在GitHub上面可以下載原始碼。etcd優雅的處理了在網路分割槽之間的master選舉,並且有很好的容錯性,包括master丟失。您的應用可以向etcd寫入資料或讀取資料。一個簡單的使用示例,實現了將儲存資料庫連結資訊和特徵存作為鍵-值儲存在etcd中。這個值可以被監控,當這些值發生變化的時候,並且允許您的應用重新配置自己。
2、為什麼介紹etcd
目前在網上能查到關於etcd的介紹只有幾篇,而且轉載來轉載去的,主要針對其raft選舉演算法進行圖解,這裡就不作介紹了。而本文主要對其的使用方法來講解,這方面相關的介紹甚少,官網https://coreos.com/把etcd的專案遷移到了GitHub,建議讀者先到這裡
3、etcd能用來做什麼
etcd是一個應用在分散式環境下的 key/value 儲存服務。利用 etcd 的特性,應用程式可以在叢集中共享資訊、配置或作服務發現,etcd 會在叢集的各個節點中複製這些資料並保證這些資料始終正確。我們知道,etcd是CoreOs輕量級linux作業系統中最重要的組成部分,CoreOS是一個基於Docker的輕量級容器化Linux發行版,專為大型資料中心而設計,旨在通過輕量的系統架構和靈活的應用程式部署能力簡化資料中心的維護成本和複雜度。CoreOS作為Docker生態圈中的重要一員,日益得到各大雲服務商的重視。另外etcd的開源性質使得它可以很容易被我們的專案所引進,利用它我們可以實現服務共享,可以很輕鬆的實現客戶端和伺服器進行通訊,在此基礎上我們可以定製各種各樣的服務。下面讓我們趕緊看看如何使用etcd吧。
4、etcd提供的HTTP RESTful API
etcd支援http RESTful API,支援get查詢,post,delete,put等操作。為了便於理解,可將它儲存資料的框架看做一個檔案系統,可以建立目錄和“檔案”,每個“檔案”名就是一個key,每個“檔案”的內容就是它的value,目錄沒有value只能包含子目錄或者“檔案”,可以通過RESTful API來獲取這些key的值或者設定這些key的值。
5、etcd命令列介面使用
*執行一個單一的機器叢集即啟動本地etcd服務
解壓上面下載的etcd軟體包,執行etcd檔案./etcd
*獲取etcd的版本號
curl -L http://127.0.0.1:2379/version
*設定一個key的value
curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="Hello world"
*獲取一個key的value
curl http://127.0.0.1:2379/v2/keys/message
*改變一個key的value
curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="Hello etcd"
*刪除一個key節點
curl http://127.0.0.1:2379/v2/keys/message -XDELETE
*使用ttl(即設定一個key的值並給這個key加一個生命週期,當超過這個時間該值沒有被訪問則自動被刪除)
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl=5
*等待一個值的變化
curl http://127.0.0.1:2379/v2/keys/foo?wait=true
該命令呼叫之後會阻塞程序,直到這個值發生變化才能返回,當改變一個key的值,或者刪除等操作發生時,該等待就會返回
特別注意,在變化發生度較高的情況下,最好把這種變化結果交給另外一個執行緒來處理,監控執行緒立即返回繼續監控變化情況,當然etcd也提供了獲取歷史變化的命令,這個命令僅為丟失監聽事件的情況下的補救方案。
*建立一個目錄
curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d dir=true
*列舉一個目錄
curl http://127.0.0.1:2379/v2/keys/dir
*遞迴列舉一個目錄
curl http://127.0.0.1:2379/v2/keys/dir?recursive=true
到這裡我們可以組合以上的諸多用法實現自己想要的功能。例如監控一個目錄下的所有key的變化,包括子目錄的。可以使用命令:
curl http://127.0.0.1:2379/v2/keys/dir?recursive=true&wait=true
*刪除一個目錄
curl 'http://127.0.0.1:2379/v2/keys/dir?dir=true' -XDELETE
命令列介面就介紹這麼多,詳細可以參考下載的安裝包裡文件裡面的api.md檔案有更加詳細的介紹。
6、c++封裝libcurl實現etcd資料操作
libcurl為c語言提供了一套HTTP RESTful API例如要實現上面的獲取一個值的方法:
#include <curl/curl.h>
#include <jsoncpp/json/json.h>
#include <iostream>
using namespace std;
using namespace Json;
size_t process_data(void *buffer, size_t size, size_t nmemb, void *user_p)
{
/* 獲取json的value */
Value root;
Value node;
Reader reader;
FastWriter writer;
string json = (char*)buffer;
if(!reader.parse(json, root))
{
cout << "parse json error" << endl;
return 0;
}
string nodeString = writer.write(root["node"]);
if(!reader.parse(nodeString, node))
{
cout << "parse json error" << endl;
return 0;
}
cout << node["value"] << endl;
return 0;
}
int main(int argc, char **argv)
{
//初始化libcurl
CURLcode return_code;
return_code = curl_global_init(CURL_GLOBAL_SSL);
if (CURLE_OK != return_code)
{
cerr << "init libcurl failed." << endl;
return -1;
}
// 獲取easy handle
CURL *easy_handle = curl_easy_init();
if (NULL == easy_handle)
{
cerr << "get a easy handle failed." << endl;
curl_global_cleanup();
return -1;
}
char * buff_p = NULL;
// 設定easy handle屬性
curl_easy_setopt(easy_handle, CURLOPT_URL, "http://127.0.0.1/v2/keys/message1");
curl_easy_setopt(easy_handle, CURLOPT_PORT, 2379);
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, &process_data);
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, buff_p);
// 執行資料請求
curl_easy_perform(easy_handle);
// 釋放資源
curl_easy_cleanup(easy_handle);
curl_global_cleanup();
return 0;
}
由於etcd返回的資料都是以json的形式,所以程式碼中還增加了一段對json的處理。實現設定一個key的功能:
#include <curl/curl.h>
#include <stdio.h>
#include <string.h>
int etcd_set(char *key, char *value, char *token)
{
#define URL_MAX_LEN 50
#define VALUE_LEN 1024
//初始化libcurl
CURLcode return_code;
char etcd_url[URL_MAX_LEN];
char etcd_value[VALUE_LEN];
return_code = curl_global_init(CURL_GLOBAL_SSL);
if (CURLE_OK != return_code)
{
//cerr << "init libcurl failed." << endl;
printf("init libcurl failed\n");
return -1;
}
sprintf(etcd_url, "http://127.0.0.1:2379/v2/keys%s", key);
sprintf(etcd_value, "value=%s", value);
// 獲取easy handle
CURL *easy_handle = curl_easy_init();
if (NULL == easy_handle)
{
//cerr << "get a easy handle failed." << endl;
printf("get a easy handle failed.\n");
curl_global_cleanup();
return -1;
}
// 設定easy handle屬性
curl_easy_setopt(easy_handle, CURLOPT_URL, etcd_url);
curl_easy_setopt(easy_handle, CURLOPT_POST, 1);
curl_easy_setopt(easy_handle, CURLOPT_POSTFIELDS, etcd_value);
curl_easy_setopt(easy_handle, CURLOPT_CUSTOMREQUEST, "PUT");
// 執行資料請求
curl_easy_perform(easy_handle);
// 釋放資源
curl_easy_cleanup(easy_handle);
curl_global_cleanup();
return 0;
}
int main(void)
{
etcd_set("/put", "2l", NULL);
return 0;
}
監控一個值的變化。這個在上面獲取一個key的值的基礎上來實現,只需要改變其url,其它不做改動即可。
#include <curl/curl.h>
#include <jsoncpp/json/json.h>
#include <iostream>
using namespace std;
using namespace Json;
size_t process_data(void *buffer, size_t size, size_t nmemb, void *user_p)
{
/* 獲取json的value */
Value root;
Value node;
Reader reader;
FastWriter writer;
string json = (char*)buffer;
if(!reader.parse(json, root))
{
cout << "parse json error" << endl;
return 0;
}
string nodeString = writer.write(root["node"]);
if(!reader.parse(nodeString, node))
{
cout << "parse json error" << endl;
return 0;
}
cout << node["value"] << endl;
return 0;
}
int main(int argc, char **argv)
{
//初始化libcurl
CURLcode return_code;
return_code = curl_global_init(CURL_GLOBAL_SSL);
if (CURLE_OK != return_code)
{
cerr << "init libcurl failed." << endl;
return -1;
}
// 獲取easy handle
CURL *easy_handle = curl_easy_init();
if (NULL == easy_handle)
{
cerr << "get a easy handle failed." << endl;
curl_global_cleanup();
return -1;
}
char * buff_p = NULL;
// 設定easy handle屬性
curl_easy_setopt(easy_handle, CURLOPT_URL, "http://127.0.0.1/v2/keys/message1?wait=true");
curl_easy_setopt(easy_handle, CURLOPT_PORT, 2379);
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, &process_data);
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, buff_p);
// 執行資料請求
curl_easy_perform(easy_handle);
// 釋放資源
curl_easy_cleanup(easy_handle);
curl_global_cleanup();
return 0;
}
以上是我實現的簡單的etcd介面,讀者可以通過本部落格或者根據libcurl提供的http RESTful API來實現etcd支援的更多的介面。
另外,基於etcd的高階語言介面在GitHub上已有相當多的開源工程,讀者感興趣可以到這裡去下載檢視——>高階語言封裝的etcd介面。
7、總結
因為工作需要研究了一段時間,特記錄在此,希望對大家有所幫助。