etcd 框架實踐【Java 版】

FunTester發表於2024-07-15

前文分享 etcd 框架 Go 語言的實踐,今天分享一下 Java 客戶端的不分。再分享之前,先簡單聊一下我查閱的資料的現狀,以方便各位再開始 Java 客戶端學習之前,有個心理預期。

etcd 本身是 Go 語言編寫的,所以在語言支援上,Go 語言是支援的最好的。其他的就差強人意,這種場景有點像 Web3j ,有人再維護,但是從使用便捷程度上,總是不能一帆風順直接上手。

而且還有一個原因,etcd 的 Java 實現庫太多了,各種庫之間的細微差異也能讓我搜尋資料的時候難以準確找到最佳實踐及其原理介紹。

大多數實現庫都用了大量的非同步操作,語法跟 Web3j 類似,我也不確定是哪種設計模式,如果你有 Web3j 使用經驗,相信會更加容易上手。

Java 客戶端比較

特性 jetcd etcd4j spring-cloud-kubernetes vertx-etcd-client
維護者 etcd-io (CoreOS) jurmous Spring Cloud Eclipse Vert.x
etcd 相容性 v3 API 主要支援 v2 API v3 API v3 API
非同步支援
依賴 gRPC Netty Spring Cloud Vert.x
特點 官方支援,全面的功能 輕量級,簡單易用 與 Spring Cloud 整合 與 Vert.x 生態系統整合
適用場景 大型專案,需要全面功能 簡單使用,遺留系統 Spring Cloud 專案 Vert.x 專案
Watch 支援
事務支援 有限 透過 Spring 抽象
效能 中等 依賴 Spring 抽象
社群活躍度 中等
文件質量 詳細 一般 詳細 詳細
學習曲線 中等 高(如果不熟悉 Spring) 中等

詳細比較

  1. jetcd
    • 優點:
      • 官方支援,與 etcd 版本同步更新
      • 全面支援 etcd v3 API
      • 效能優秀,適合大規模生產環境
    • 缺點:
      • 依賴較重(gRPC)
      • 學習曲線可能稍陡
  2. etcd4j
    • 優點:
      • 輕量級,容易整合
      • API 簡單直觀
    • 缺點:
      • 主要支援 etcd v2 API,對 v3 支援有限
      • 社群更新較慢
      • 不適合需要 v3 API 特性的新專案
  3. spring-cloud-kubernetes
    • 優點:
      • 與 Spring Cloud 和 Kubernetes 生態系統深度整合
      • 提供服務發現和配置管理功能
    • 缺點:
      • 依賴 Spring 生態系統,不適合非 Spring 專案
      • 可能引入不必要的複雜性(如果只需要簡單的 etcd 客戶端)
  4. vertx-etcd-client
    • 優點:
      • 與 Vert.x 生態系統整合
      • 非阻塞 API,適合高併發場景
    • 缺點:
      • 與 Vert.x 繫結,不適合非 Vert.x 專案
      • 社群相對較小

Java 客戶端實踐

下面我選擇 jetcd 作為實現庫,首先我們新增依賴專案:

<dependency>  
    <groupId>io.etcd</groupId>  
    <artifactId>jetcd-core</artifactId>  
    <version>0.7.0</version>  
</dependency>  
<dependency>  
    <groupId>com.google.guava</groupId>  
    <artifactId>guava</artifactId>  
    <version>31.1-jre</version>  
</dependency>

如果你再執行當中遇到了 Exception in thread "main" java.lang.NoClassDefFoundError: 此類錯誤,請檢查服務端版本,gRPC 版本,客戶端版本,以及依賴項缺失。這也是勸退的原因之一。

接下來我們來看 Case,除了讀寫以外,我增加了監聽的用例。總體來講,語法比較熟悉(我用過 Web3j ),下面是兩個簡單的例子,用來演示 jetcd 的基本使用。

package com.funtest.temp  

import com.funtester.frame.SourceCode  
import io.etcd.jetcd.ByteSequence  
import io.etcd.jetcd.Client  
import io.etcd.jetcd.Watch  
import io.etcd.jetcd.kv.GetResponse  
import io.etcd.jetcd.watch.WatchEvent  

import java.nio.charset.Charset  
import java.nio.charset.StandardCharsets  
import java.util.concurrent.CompletableFuture  

class TtcdTest extends SourceCode {  

    static Charset defaultCharset = StandardCharsets.UTF_8  

    // 建立客戶端, 連線etcd  
    static def client = Client.builder().endpoints("http://localhost:2379").build()  

    // 建立KV客戶端, 用於讀寫資料  
    static def kVClient = client.getKVClient()  

    static def watchClient = client.getWatchClient()  

    /**  
     * 監聽etcd中的key變化, 有變化時列印出來  
     */  
    static watch() {  
        def key = toByteSequence("key")// 監聽的key  
        Watch.Listener listener = Watch.listener(watchResponse -> {// 監聽器  
            for (WatchEvent event : watchResponse.getEvents()) {// 事件  
                println("watch change ------------------")// 列印  
                println("修改的型別Event type: " + event.getEventType());// 事件型別, PUT, DELETE  
                println("修改的Key: " + event.getKeyValue().getKey().toString(StandardCharsets.UTF_8));// 修改的Key, ByteSequence轉字串  
                println("修改後Value: " + event.getKeyValue().getValue().toString(StandardCharsets.UTF_8));// 修改後的Value, ByteSequence轉字串  
            }  
        });  
      watchClient.watch(key, listener)// 監聽key, 有變化時觸發監聽器  
    }  

    /**  
     * 寫入資料, 讀取資料  
     * @return  
     */  
    static writeRead() {  
        kVClient.put(toByteSequence("key"), toByteSequence("FunTester")).get()// 寫入key-value  
        CompletableFuture<GetResponse> getFuture = kVClient.get(toByteSequence("key"))// 讀取key-value  
        GetResponse response = getFuture.get()// 獲取結果, 阻塞等待, 直到獲取到結果  
        println("Value: " + response.getKvs().get(0).getValue().toString())// 列印結果  
    }  

    /**  
     * 字串轉ByteSequence  
     * @param str  
     * @param Charset  
     * @return  
     */  
    static ByteSequence toByteSequence(String str, Charset = defaultCharset) {  
        return ByteSequence.from(str, defaultCharset);  
    }  
}

下面我們來依次執行兩個方法:

public static void main(String[] args) {
    watch()
    writeRead()
}

下面是控制檯列印:

watch change ------------------
修改的型別Event type: PUT
修改的Key: key
修改後Value: FunTester
Value: FunTester

可以看到是滿足預期的。但是問題來了,JVM 程序就是不退出,比較尷尬,即使我們加上關閉客戶端的方法 client.close() 也不行,開啟執行緒轉儲之後發現好幾個 RUNNABLE 的執行緒,還有一個 forkjoin 執行緒池,現象跟 Web3j 很像,但是這次跟 Netty 相關,我也懶得深究原因了。

  • 2021 年原創合集
  • 2022 年原創合集
  • 2023 年原創合集
  • 服務端功能測試
  • 效能測試專題
  • Java、Groovy、Go、Python
  • 單元&白盒&工具合集
  • 測試方案&BUG&爬蟲&UI 自動化
  • 測試理論雞湯
  • 社群風采&影片合集
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。

相關文章