ITPub博客

首页 > 数据库 > 数据库开发技术 > etcd框架实践【Java版】

etcd框架实践【Java版】

原创 数据库开发技术 作者:FunTester 时间:2024-07-15 08:13:00 0 删除 编辑

前文分享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
  • 依赖较重(gRPC)
  • 学习曲线可能稍陡
  • 官方支持,与 etcd 版本同步更新
  • 全面支持 etcd v3 API
  • 性能优秀,适合大规模生产环境
  • 优点:
  • 缺点:
  • etcd4j
    • 主要支持 etcd v2 API,对 v3 支持有限
    • 社区更新较慢
    • 不适合需要 v3 API 特性的新项目
    • 轻量级,容易集成
    • API 简单直观
    • 优点:
    • 缺点:
  • spring-cloud-kubernetes
    • 依赖 Spring 生态系统,不适合非 Spring 项目
    • 可能引入不必要的复杂性(如果只需要简单的 etcd 客户端)
    • 与 Spring Cloud 和 Kubernetes 生态系统深度集成
    • 提供服务发现和配置管理功能
    • 优点:
    • 缺点:
  • vertx-etcd-client
    • 与 Vert.x 绑定,不适合非 Vert.x 项目
    • 社区相对较小
    • 与 Vert.x 生态系统集成
    • 非阻塞 API,适合高并发场景
    • 优点:
    • 缺点:

    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 相关,我也懒得深究原因了。


    原文作者:FunTester;如有侵权,请联系管理员删除。

    上一篇: JDBC 最佳实践
    请登录后发表评论 登录
    全部评论

    注册时间:2019-07-07

    • 博文量
      1037
    • 访问量
      420671