Backend Development 10 min read

Comparison and Practical Guide to Java etcd Clients

This article compares popular Java etcd client libraries, evaluates their features, performance, and suitability, and provides a hands‑on tutorial using jetcd with Maven dependencies, code examples for watching and reading keys, and discusses runtime considerations such as thread handling.

FunTester
FunTester
FunTester
Comparison and Practical Guide to Java etcd Clients

Previously I shared a Go implementation of etcd; now I discuss Java clients, noting that Go has the best support while Java options vary in quality, often resembling Web3j in asynchronous design and documentation.

Java Client Comparison

Feature

jetcd

etcd4j

spring-cloud-kubernetes

vertx-etcd-client

Maintainer

etcd-io (CoreOS)

jurmous

Spring Cloud

Eclipse Vert.x

etcd Compatibility

v3 API

mainly v2 API

v3 API

v3 API

Async Support

Yes

Yes

Yes

Yes

Dependency

gRPC

Netty

Spring Cloud

Vert.x

Features

Official support, full functionality

Lightweight, easy to use

Spring Cloud integration

Vert.x ecosystem integration

Use Cases

Large projects needing full features

Simple usage, legacy systems

Spring Cloud projects

Vert.x projects

Watch Support

Yes

Yes

Yes

Yes

Transaction Support

Yes

Limited

Via Spring abstraction

Yes

Performance

High

Medium

Depends on Spring abstraction

High

Community Activity

High

Low

High

Medium

Documentation Quality

Detailed

Average

Detailed

Detailed

Learning Curve

Medium

Low

High (if unfamiliar with Spring)

Medium

Detailed Comparison

jetcd Heavy dependency (gRPC) Potentially steep learning curve Official support, synchronized with etcd releases Full v3 API support Excellent performance for large‑scale production Pros: ... Cons: ...

etcd4j Primarily supports v2 API, limited v3 features Community updates are slower Not suitable for new projects requiring v3 features Lightweight and easy to integrate Simple, intuitive API Pros: ... Cons: ...

spring-cloud-kubernetes Depends on Spring ecosystem, unsuitable for non‑Spring projects May introduce unnecessary complexity for simple etcd usage Deep integration with Spring Cloud and Kubernetes Provides service discovery and configuration management Pros: ... Cons: ...

vertx-etcd-client Bound to Vert.x, not ideal for non‑Vert.x projects Smaller community Integrates with Vert.x ecosystem Non‑blocking API, suitable for high‑concurrency scenarios Pros: ... Cons: ...

Java Client Practice

Below I choose jetcd as the implementation library and add the following Maven dependencies:

<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>

If you encounter a Exception in thread "main" java.lang.NoClassDefFoundError , verify server version, gRPC version, client version, and missing dependencies.

The following example demonstrates watching a key and performing basic write/read operations using 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
  // Create client, connect to etcd
  static def client = Client.builder().endpoints("http://localhost:2379").build()
  static def kVClient = client.getKVClient()
  static def watchClient = client.getWatchClient()

  /**
   * Watch key changes in etcd and print them
   */
  static watch() {
    def key = toByteSequence("key")
    Watch.Listener listener = Watch.listener(watchResponse -> {
      for (WatchEvent event : watchResponse.getEvents()) {
        println("watch change ------------------")
        println("Event type: " + event.getEventType())
        println("Key: " + event.getKeyValue().getKey().toString(StandardCharsets.UTF_8))
        println("Value: " + event.getKeyValue().getValue().toString(StandardCharsets.UTF_8))
      }
    })
    watchClient.watch(key, listener)
  }

  /**
   * Write and read data
   */
  static writeRead() {
    kVClient.put(toByteSequence("key"), toByteSequence("FunTester")).get()
    CompletableFuture
getFuture = kVClient.get(toByteSequence("key"))
    GetResponse response = getFuture.get()
    println("Value: " + response.getKvs().get(0).getValue().toString())
  }

  /**
   * Convert String to ByteSequence
   */
  static ByteSequence toByteSequence(String str, Charset = defaultCharset) {
    return ByteSequence.from(str, defaultCharset);
  }
}

Execute the two methods:

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

Console output shows the expected watch event and value retrieval, but the JVM does not exit even after calling client.close() , leaving several RUNNABLE threads and a fork‑join pool, similar to issues seen with Web3j and Netty.

Server Feature Test Performance Test Topics Java, Groovy, Go, Python Unit & White‑box & Tools Collection Testing Strategies, Bugs, Crawlers, UI Automation Testing Theory Soup Community Highlights & Video Collection
JavadistributedETCDClientKey-Value Storejetcd
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.