天天看點

[譯] Java 和 etcd: 因為 jetcd 最終走到了一起

原文位址:Java and etcd: together at last, with jetcd

原文作者:Fanmin Shi

譯文出自:掘金翻譯計劃

本文永久連結:github.com/xitu/gold-m…

譯者:mingxing

校對者:xiantang

可靠的鍵值存儲為分布式系統提供了一緻性配置和協調的公共基礎。etcd 項目就是一個這樣的系統。這是一個由 CoreOS 建立的開源鍵值存儲系統。它是許多生産級分布式系統的核心元件和 Kubernetes 等項目的資料存儲中心。

Java 已經通過在包括 Hadoop 生态系統、Cassandra 資料存儲和雲基礎設施技術棧中的使用而證明了自己是一種流行的分布式系統語言。此外,它仍然是一種非常流行的語言。可以看看在谷歌趨勢的統計資料中,Java 仍然占據主導地位:

[譯] Java 和 etcd: 因為 jetcd 最終走到了一起

就谷歌搜尋結果而言,Java 仍然比 Microsoft 的 .Net 甚至 JavaScript 語言更受歡迎

面對着 Java 的流行及其在分布式系統中的普遍使用,我們認為對于 Java 開發來說,etcd 也應該作為後端基礎被使用到。jetcd 這個新的 etcd 用戶端的出現,将 etcd v3 API 帶到了 Java 中。

通過使用 jetcd,Java 應用程式可以使用包裝了 etcd 的原生 gRPC 協定的智能 API 來與 etcd 進行純粹的互動。該 API 提供了僅在 etcd 上可用的表達性分布式特性。更重要的是,通過直接支援更多的語言,使用新的使用模式更容易為 etcd 編寫新的應用程式,進而幫助 etcd 變得更加穩定和可靠。

初級入門

你可以通過建構并運作一個名為 jetcdctl 的小例子程式來試用 jetcd,該程式使用了 jetcd 去通路 etcd。對于更進一步的 jetcd 項目來說,jetcdctl 示例也是一個很好的起點。要繼續學習,你還需要同時安裝 Git 和 Java。

首先,克隆 jetcd 庫來擷取 jetcd 源碼,然後使用 Maven 來建構 jetcd-simple-ctl 吧:

$ git clone https://github.com/coreos/jetcd.git
$ cd jetcd/jetcd-examples/jetcd-simple-ctl
$ ./mvnw clean package           

建構并準備好運作 jetcdctl 之後,下載下傳一個 etcd 發行版并在本地啟動一個 etcd 服務。(譯者注:若以下 “go get” 指令無法正常運作,可以參考這裡的資料):

# build with “go get github.com/coreos/etcd/cmd/etcd”
$ etcd &           

接下來,使用 jetcdctl 将 123 寫入 abc,與本地 etcd 伺服器進行通信:

$ java -jar target/jetcdctl.jar put abc 123
21:39:06.126|INFO |CommandPut - OK           

你可以通過讀取 abc 來确認寫入 etcd 的 put 指令的正确性:

$ java -jar target/jetcdctl.jar get abc 21:41:00.265|INFO |CommandGet - abc 21:41:00.267|INFO |CommandGet - 123           

我們已經通過 get 和 put keys 示範了 jetcd 的基本功能。現在,讓我們進一步研究如何在代碼中使用 jetcd 吧。

更好的 watches(觀察)特性

jetcd API 可以友善地管理 etcd 的底層 gRPC 協定。一個例子是 streaming key 事件,其中用戶端觀察 key,etcd 服務端不斷地往用戶端發回更新資訊。jetcd 用戶端管理着一個低級别的 gRPC 流,用來優雅地處理斷開連接配接,并向使用者呈現一個無縫的事件流。

如果 jetcd 應用程式希望接收到一個 key 的所有更新,它将使用 watch API 來建立一個 Watcher:

Watcher watch(ByteSequence key)           

Watcher 的 listen 方法從 etcd 中讀取 WatchResponse 消息。每個 WatchResponse 包含被監視 key 上的最新事件序列。如果沒有任何事件,則 listen 被阻塞,直到有更新為止。listen 方法是可靠的;它不會在調用之間删除任何事件,即使在斷開連接配接的情況下:

WatchResponse listen() throws InterruptedException           

總之,用戶端建立一個 Watcher,然後使用 listen 來等待事件。下面是在 key abc 上進行觀察的代碼,列印觀察到的 key 和 value,直到 listen 抛出異常:總之,用戶端建立一個 Watcher,然後使用 listen 來等待事件。下面是觀察 key abc 的代碼,列印 key 和 value,直到 listen 抛出異常:

Client client = Client.builder().endpoints(“http://127.0.0.1:2379).build();
Watcher watcher = client.getWatchClient().watch(ByteSequence.fromString("abc"));
while (true) {
    for (WatchEvent event : watcher.listen().getEvents()) {
        KeyValue kv = event.getKeyValue();
        System.out.println(event.getEventType());
        System.out.println(kv.getKey().toStringUtf8());
        System.out.println(kv.getValue().toStringUtf8());
    }
}           

将此特性與 Apache 基金會中與 etcd 對标的 ZooKeeper 進行比較。從 ZooKeeper 3.4.10 開始,watch 就已經是一次性觸發器,這意味着一旦收到一個 watch 事件,您必須設定一個新的 watch,以便在将來發生更改時得到通知。要傳輸密鑰事件,可會斷必須與叢集聯系,為每個新事件注冊一個新的觀察者。

要在 key 更新時連續列印 key 的内容,ZooKeeper 應用程式首先建立一個 Watcher 來偵聽 WatchedEvent 消息。觀察程式實作了一個事件回調方法 process,當 key 發生更改時就會調用該方法。要在事件中注冊興趣,觀察程式需要添加到 exists 方法中,該方法擷取 key 的中繼資料(如果有的話)。當 key 發生變化時,觀察者的 process 方法就會調用 getData 來檢索 key 的值,然後再次注冊相同的觀察者來接收未來的更改,如下所示:

key = “/abc”;
Watcher w = new Watcher() {
  public void process(WatchedEvent event) {
    try {
      System.out.println(event.getType());
      System.out.println(event.getPath());
      if (event.getType() != EventType.NodeDeleted) {
        System.out.println(new String(zk.getData(event.getPath(), false, null)));
      }
      zk.exists(key, this);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
};
zk.exists(key, w);           

與 jetcd 示例不同,ZooKeeper 代碼不能保證它觀察所有更改,因為在監視程式接收事件和發送請求以擷取新監視之間存在延遲。例如,在執行 process 和調用 exists 以注冊新監視程式之間發生了一個事件。由于沒有注冊任何觀察程式,是以該事件永遠不會被觸發,并且會丢失。

即使假設所有事件都已觸發,代碼仍然可能破壞事件流。沒有 etcd 提供的多版本并發控制,就無法通路曆史 key。如果 key value 在接收事件和擷取資料之間發生了變化,代碼将列印出最新的值,而不是與 watch 事件關聯的值。更糟的是,事件沒有附帶修訂資訊;無法确定 key 是來自事件還是來自 future 傳回。

v0.0.1 版本以及未來計劃

從 v0.0.1 開始,jetcd 支援大多數應用程式需要的鍵值存儲。這些原語可以作為複雜模式(如分布式隊列、barriers 等)的建構塊。在未來,jetcd 将能夠使用 etcd 的本地鎖和上司人選舉 rpc 進行叢集範圍的标準化分布式協調。

jetcd 設計目的是易于使用,同時還能夠利用 etcd 的先進功能。它是開源的,并且正在活躍開發中,歡迎社群的貢獻和回報。我們可以在 GitHub 上找到它,位址是github.com/coreos/jetc…。