记录java中zookeeper客户端ZkClient一个小坑

发布时间 2023-07-24 16:51:16作者: 雨山木风
项目中使用org.I0Itec.zkclient.ZkClient库作为zookeeper的连接工具,一直很稳定。不过有个奇怪的问题,从ZooInspetor中连接Zookeeper,看NodeData,会发现数据像图中这样,字符串前面有一段奇怪的字符:

一直不清楚"t%"这些像乱码一样的字符是什么东西。因为项目运行的比较稳定,也没报错,就一直没管。

最近涉及到跨语言,要使用Golang去连接相同的zookeeper,就有问题了。去研究了一下,倒是比较容易找到原因。下面是示例代码:

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;

public class ZooKeeperClient {

    private static final String ZOOKEEPER_SERVER = "localhost:2181";
    private static final String NODE_PATH = "/example_node";

    public static void main(String[] args) {
        // 创建一个 ZkClient 实例
        ZkClient zkClient = new ZkClient(ZOOKEEPER_SERVER);
    // 默认数据编码为 SerializableSerializer
    // zkClient.setZkSerializer(new BytesPushThroughSerializer());
    // 写入节点数据
        zkClient.writeData(NODE_PATH, "Hello, ZooKeeper!");
        // 读取节点数据
        String nodeData = zkClient.readData(NODE_PATH);
        System.out.println("节点数据:" + nodeData);
        // 注册节点数据监听器
        zkClient.subscribeDataChanges(NODE_PATH, new IZkDataListener() {
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {
                System.out.println("节点数据变化:" + data);
            }
            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                System.out.println("节点数据被删除");
            }
        });
        // 修改节点数据,触发数据变化事件
        zkClient.writeData(NODE_PATH, "Updated data");
        // 等待一段时间,观察数据变化事件
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 删除节点
        zkClient.delete(NODE_PATH);
        // 关闭 ZkClient
        zkClient.close();
    }
}

问题就出在"默认数据编码为 SerializableSerializer"这里了。查看ZkCLient构造方法的源码:

 

new ZkClient(ZOOKEEPER_SERVER)构造的zk客户端,默认的使用的序列化策略为SerializableSerializer,它用于将节点数据以 Java 的序列化方式进行编码和解码。所以才导致了使用工具查看节点时,发现节点数据前面带有一段意义不明的字符串。

我的解决方法是:

1.zkClient.setZkSerializer(new BytesPushThroughSerializer());
2.zkClient.writeData(NODE_PATH, "Hello, ZooKeeper!".getBytes("UTF-8"));
3.String nodeData = new String(zkClient.readData(NODE_PATH), "UTF-8");

BytesPushThroughSerializer的实现也很简单,它直接将字节数组作为节点数据进行写入和读取,不做任何序列化和反序列化操作。如何有自定义的要求也可以自定义实现自己的 ZkSerializer 接口,根据具体需求选择适合的编码方式。至此问题解决