zookeeper Java client
Project construction
The official client of zookeeper is not separated from the server code. They are the same jar file, so we can directly introduce zookeeper maven. Please keep the version here consistent with the server version, otherwise there will be many compatibility problems
<dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.5.8</version> </dependency>
Create client instance
- Defines the wrapper class to send
package zookeeper.client; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @ToString @NoArgsConstructor public class MyConfig { private String key; private String name; }
- Define zookeeper client
package zookeeper.client; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.zookeeper.*; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @Slf4j public class ConfigCenter { private final static String CONNECT_STR="192.168.1.104:2181"; private final static Integer SESSION_TIMEOUT=30*1000; private static ZooKeeper zooKeeper=null; private static CountDownLatch countDownLatch=new CountDownLatch(1); public static void main(String[] args) throws IOException, InterruptedException, KeeperException { zooKeeper=new ZooKeeper(CONNECT_STR, SESSION_TIMEOUT, new Watcher() { @Override public void process(WatchedEvent event) { if (event.getType()== Event.EventType.None && event.getState() == Event.KeeperState.SyncConnected){ log.info("Connection established"); countDownLatch.countDown(); } } }); countDownLatch.await(); MyConfig myConfig = new MyConfig(); myConfig.setKey("anykey"); myConfig.setName("anyName"); ObjectMapper objectMapper=new ObjectMapper(); byte[] bytes = objectMapper.writeValueAsBytes(myConfig); String s = zooKeeper.create("/myconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Watcher watcher = new Watcher() { @SneakyThrows @Override public void process(WatchedEvent event) { if (event.getType()== Event.EventType.NodeDataChanged && event.getPath()!=null && event.getPath().equals("/myconfig")){ log.info(" PATH:{} Data changes have occurred" ,event.getPath()); byte[] data = zooKeeper.getData("/myconfig", this, null); MyConfig newConfig = objectMapper.readValue(new String(data), MyConfig.class); log.info("Data changes: {}",newConfig); } } }; byte[] data = zooKeeper.getData("/myconfig", watcher, null); MyConfig originalMyConfig = objectMapper.readValue(new String(data), MyConfig.class); log.info("raw data: {}", originalMyConfig); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); } }
- log4j.properties configuration class
# Copyright 2012 The Apache Software Foundation # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Define some default values that can be overridden by system properties zookeeper.root.logger=ERROR, CONSOLE zookeeper.console.threshold=INFO zookeeper.log.dir=. zookeeper.log.file=zookeeper.log zookeeper.log.threshold=INFO zookeeper.log.maxfilesize=256MB zookeeper.log.maxbackupindex=20 zookeeper.tracelog.dir=${zookeeper.log.dir} zookeeper.tracelog.file=zookeeper_trace.log log4j.rootLogger=${zookeeper.root.logger} # # console # Add "console" to rootlogger above if you want to use this # log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold} log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n # # Add ROLLINGFILE to rootLogger to get log file output # log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold} log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file} log4j.appender.ROLLINGFILE.MaxFileSize=${zookeeper.log.maxfilesize} log4j.appender.ROLLINGFILE.MaxBackupIndex=${zookeeper.log.maxbackupindex} log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n # # Add TRACEFILE to rootLogger to get log file output # Log TRACE level and above messages to a log file # log4j.appender.TRACEFILE=org.apache.log4j.FileAppender log4j.appender.TRACEFILE.Threshold=TRACE log4j.appender.TRACEFILE.File=${zookeeper.tracelog.dir}/${zookeeper.tracelog.file} log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout ### Notice we are including log4j's NDC here (%x) log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L][%x] - %m%n log4j.logger.zookeeper.client=INFO
test result
Parameter description for creating zookeeper instance
Parameter name | meaning |
---|---|
connectString | The list of ZooKeeper servers is composed of host:port strings separated by English commas. Each represents a ZooKeeper machine, such as host1:port1,host2:port2,host3:port3. In addition, you can also set the root directory after the client connects to the ZooKeeper in the connectString by adding this root directory after the host:port string, for example, host1:port1,host2:port2,host3:port3 / ZK base. This specifies that all operations on the ZooKeeper will be based on this root directory after the client connects to the ZooKeeper server. For example, when the client operates on / sub node, it eventually creates / ZK node / sub node. This directory is also called Chroot, that is, the client isolation namespace. |
sessionTimeout | The timeout of the session is an integer value in milliseconds. There is the concept of session in ZooKeeper. In a session cycle, the ZooKeeper client and server will maintain the effectiveness of the session through the heartbeat detection mechanism. Once there is no effective heartbeat detection within the sessionTimeout time, the session will fail. |
watcher | ZooKeeper allows the client to pass in an implementation class object of the interface watcher (org.apache. zookeeper. Watcher) in the construction method as the default watcher event notification processor. Of course, this parameter can be set to null to indicate that the default watcher processor does not need to be set. |
canBeReadOnly | This is a boolean parameter, which is used to identify whether the current session supports the "read-only" mode. By default, in a ZooKeeper cluster, if a machine loses network connection with more than half or more machines in the cluster, the machine will no longer process client requests (including read-write requests). However, in some usage scenarios, when such failures occur in the ZooKeeper server, we still hope that the ZooKeeper server can provide read services (of course, the write service cannot be provided) - this is ZooKeeper's "read only" mode. |
sessionId and sessionPasswd | Represents the session ID and session secret key respectively. These two parameters can uniquely determine a session. At the same time, the client can reuse the client session by using these two parameters, so as to achieve the effect of restoring the session. The specific method is to call the following two interfaces of the ZooKeeper object instance to obtain the ID and password of the current session when connecting to the ZooKeeper server for the first time Secret key: long getSessionId(); byte[]getSessionPasswd(); after these two parameter values are obtained, you can pass in the constructor the next time you create a ZooKeeper object instance |
Demonstrate the addition, deletion, query and modification of zookeeper node in java client
package zookeeper.client; import lombok.extern.slf4j.Slf4j; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.junit.After; import org.junit.Before; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @Slf4j public abstract class StandaloneBase { private static final String CONNECT_STR = "192.168.1.104:2181"; private static final int SESSION_TIMEOUT = 30 * 1000; private static ZooKeeper zooKeeper = null; private static CountDownLatch countDownLatch = new CountDownLatch(1); private Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if (event.getState() == Event.KeeperState.SyncConnected && event.getType() == Event.EventType.None) { countDownLatch.countDown(); log.info("Connection establishment"); } } }; @Before public void init() { try { log.info(" start to connect to zookeeper server: {}", getConnectStr()); zooKeeper = new ZooKeeper(getConnectStr(), getSessionTimeout(), watcher); log.info(" Connecting..."); countDownLatch.await(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } public static ZooKeeper getZooKeeper() { return zooKeeper; } @After public void test() { try { TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } protected String getConnectStr() { return CONNECT_STR; } protected int getSessionTimeout() { return SESSION_TIMEOUT; } }
package zookeeper.client; import lombok.extern.slf4j.Slf4j; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.junit.Test; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; @Slf4j public class AclOperations extends StandaloneBase { /** * Creating nodes in world mode * * @throws KeeperException * @throws InterruptedException */ @Test public void createWithAclTest1() throws KeeperException, InterruptedException { List<ACL> acLList = new ArrayList<ACL>(); ACL e = new ACL(); Id m_ = new Id(); m_.setId("anyone"); m_.setScheme("world"); int perms = ZooDefs.Perms.ADMIN | ZooDefs.Perms.READ; e.setId(m_); e.setPerms(perms); acLList.add(e); String s = getZooKeeper().create("/zk-node-1", "shikaiqiang".getBytes(), acLList, CreateMode.PERSISTENT); log.info("create path: {}",s); } /** * * Create node with authorization mode * @throws KeeperException * @throws InterruptedException */ @Test public void createWithAclTest2() throws KeeperException, InterruptedException { // Add authorization information to the connection getZooKeeper().addAuthInfo("digest","u400:p400".getBytes()); List<ACL> acLList = new ArrayList<ACL>(); ACL e = new ACL(); Id m_ = new Id(); m_.setId("u400:p400"); m_.setScheme("auth"); int perms = ZooDefs.Perms.ADMIN | ZooDefs.Perms.READ; e.setId(m_); e.setPerms(perms); acLList.add(e); String s = getZooKeeper().create("/zk-node-2", "shikaiqiang".getBytes(), acLList, CreateMode.PERSISTENT); log.info("create path: {}",s); } @Test public void createWithAclTest3() throws KeeperException, InterruptedException { // Add authorization information to the connection getZooKeeper().addAuthInfo("digest","u400:p400".getBytes()); byte[] data = getZooKeeper().getData("/test", false, null); log.info("GET_DATA : {}",new String(data)); } public static void main(String[] args) throws NoSuchAlgorithmException { String sId = DigestAuthenticationProvider.generateDigest("skq:123456"); System.out.println(sId); // -Dzookeeper.DigestAuthenticationProvider.superDigest=gj:X/NSthOB0fD/OT6iilJ55WJVado= } }
package zookeeper.client; import lombok.extern.slf4j.Slf4j; import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import org.junit.Test; @Slf4j public class BaseOperations extends StandaloneBase { private String first_node = "/first-node"; @Test public void testCreate() throws KeeperException, InterruptedException { ZooKeeper zooKeeper = getZooKeeper(); String s = zooKeeper.create(first_node, "first".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); log.info("Create:{}", s); } @Test public void testGetData() { Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if (event.getPath() != null && event.getPath().equals(first_node) && event.getType() == Event.EventType.NodeDataChanged) { log.info(" PATH: {} Discover changes", first_node); try { byte[] data = getZooKeeper().getData(first_node, this, null); log.info(" data: {}", new String(data)); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } }; try { byte[] data = getZooKeeper().getData(first_node, watcher, null); // log.info(" data: {}", new String(data)); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } @Test public void testSetData() throws KeeperException, InterruptedException { ZooKeeper zooKeeper = getZooKeeper(); Stat stat = new Stat(); byte[] data = zooKeeper.getData(first_node, false, stat); // int version = stat.getVersion(); zooKeeper.setData(first_node, "third".getBytes(), 0); } @Test public void testDelete() throws KeeperException, InterruptedException { // -1 means that all versions are matched and deleted directly // Any representative greater than - 1 can specify data version deletion getZooKeeper().delete(first_node, -1); } @Test public void asyncTest() { String userId = "xxx"; getZooKeeper().getData("/test", false, (rc, path, ctx, data, stat) -> { Thread thread = Thread.currentThread(); log.info(" Thread Name: {}, rc:{}, path:{}, ctx:{}, data:{}, stat:{}", thread.getName(), rc, path, ctx, data, stat); }, "test"); log.info(" over ."); } }
Apache cursor open source client
What is cursor
Cursor is a set of ZooKeeper client framework open source by netflix company and programmed in Java language. Cursor project is the most used ZooKeeper client and supports the best third-party client for ZooKeeper version. It is recommended to use. Cursor encapsulates many commonly used ZooKeeper service development functions, such as Leader election and distributed counting Device and distributed lock. This reduces most of the low-level detail development work of technicians when using ZooKeeper. In the use scenarios such as session reconnection, Watch repeated registration and various exception handling, it is more complex to use native ZooKeeper. When using cursor, because it highly encapsulates these functions, it is easier to use and does not need to be used But it reduces the development time and enhances the reliability of the program.
Actual combat of cursor
First, introduce the development packages related to the cursor framework. The first is the cursor framework package, which encapsulates the underlying API of Zookeeper. The other is the cursor receipts package, which encapsulates some advanced features of Zookeeper services, such as Cache event monitoring, voting, distributed lock and distributed Barrier. junit, lombok and, Since Zookeeper itself has a log4j logging framework, you can create a corresponding log4j configuration file and use it directly.
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.0.0</version> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-x-discovery</artifactId> <version>5.0.0</version> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.5.8</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.3</version> </dependency>
Session creation
To interact with the client and server, the first step is to create a session. The creator provides a variety of ways to create a session, such as using a static factory:
// Retry policy RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3) CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy); client.start();
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("192.168.1.104:2181") .sessionTimeoutMs(5000) // Session timeout .connectionTimeoutMs(5000) // Connection timeout .retryPolicy(retryPolicy) .namespace("base") // Include isolation name .build(); client.start();
The coding style of this code adopts streaming mode. The core class is the CuratorFramework class, which is used to define a ZooKeeper client object and use it in the subsequent context. When defining the CuratorFramework object instance, we use the CuratorFrameworkFactory factory method and specify the connectionString server address list retryPolicy, retry policy, sessionTimeoutMs session timeout, connectionTimeoutMs session creation timeout.
connectionString: server address list. When specifying the server address list, it can be one address or multiple addresses. If it is multiple addresses, each server address list is separated by commas, such as host1:port1,host2:port2,host3; port3.
retryPolicy: retry policy. When the client exits abnormally or loses connection with the server, you can set the client to reconnect to the ZooKeeper server. The cursor provides different implementation methods such as one retry and multiple retries. Within the cursor, you can judge whether to retry by judging the status code of the keeperException returned by the server Retry processing. If OK is returned, it means that there is no problem with all operations, and SYSTEMERROR indicates a system or server error.
Policy name | describe |
---|---|
ExponentialBackoffRetry | Retry a group of times, and the sleep time between retries increases |
RetryNTimes | Maximum number of retries |
RetryOneTime | Try again only once |
RetryUntilElapsed | Retry before the end of the given time |
Timeout: there are two timeout settings during the creation of the cursor client. One is sessionTimeoutMs session timeout, which is used to set the expiration time of this session on the ZooKeeper server. The other is the timeout of the connectionTimeoutMs client to create a session, which is used to limit the time for the client to initiate a session and connect to the ZooKeeper server to receive the response. sessionTimeoutMs works on the server, while connectionTimeoutMs works on the client.
Create node
In Curator, you can use the create function to create data nodes, and specify the node type (persistent node, temporary node, order node, temporary order node, persistent order node) through the withMode function. The default is the persistent node, then the forPath function is used to specify the path and data information of the node.
guaranteed: the function of this function is the same as the literal meaning. It mainly plays a role in ensuring the success of deletion. Its bottom working mode is: as long as the client's session is valid, it will continue to initiate deletion requests in the background until the data node is deleted on the ZooKeeper server.
deletingChildrenIfNeeded: after this function is specified, the system will directly delete its child nodes and the child nodes of the child nodes in a recursive manner when deleting the data node.
package zookeeper.curator; import lombok.extern.slf4j.Slf4j; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.data.Stat; import org.junit.After; import org.junit.Before; import java.util.concurrent.TimeUnit; @Slf4j public abstract class CuratorStandaloneBase { private static final String CONNECT_STR = "192.168.1.104:2181"; private static final int sessionTimeoutMs = 60 * 1000; private static final int connectionTimeoutMs = 5000; private static CuratorFramework curatorFramework; @Before public void init() { RetryPolicy retryPolicy = new ExponentialBackoffRetry(5000, 30); curatorFramework = CuratorFrameworkFactory.builder().connectString(getConnectStr()) .retryPolicy(retryPolicy) .sessionTimeoutMs(sessionTimeoutMs) .connectionTimeoutMs(connectionTimeoutMs) .canBeReadOnly(true) .build(); curatorFramework.getConnectionStateListenable().addListener((client, newState) -> { if (newState == ConnectionState.CONNECTED) { log.info("Connection succeeded!"); } }); log.info("Connecting......"); curatorFramework.start(); } public void createIfNeed(String path) throws Exception { Stat stat = curatorFramework.checkExists().forPath(path); if (stat == null) { String s = curatorFramework.create().forPath(path); log.info("path {} created! ", s); } } public static CuratorFramework getCuratorFramework() { return curatorFramework; } @After public void test() { try { TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } protected String getConnectStr() { return CONNECT_STR; } }
The method of creating a node is shown in the following code. Review what we talked about in the previous course, and describe the type of node to include, that is, temporary node or persistent node, node data information, whether the node is an ordered node and other attributes and properties.
package zookeeper.curator; import lombok.extern.slf4j.Slf4j; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.CreateMode; import org.junit.Test; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Slf4j public class CuratorBaseOperations extends CuratorStandaloneBase { // Create child nodes recursively @Test public void testCreateWithParent() throws Exception { CuratorFramework curatorFramework = getCuratorFramework(); String pathWithParent = "/node-parent/sub-node-1"; String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent); log.info("curator create node :{} successfully.", path); } // protection mode to prevent zombie nodes due to abnormal reasons @Test public void testCreate() throws Exception { CuratorFramework curatorFramework = getCuratorFramework(); String forPath = curatorFramework .create() .withProtection() .withMode(CreateMode.EPHEMERAL_SEQUENTIAL). forPath("/curator-node", "some-data".getBytes()); log.info("curator create node :{} successfully.", forPath); } @Test public void testGetData() throws Exception { CuratorFramework curatorFramework = getCuratorFramework(); byte[] bytes = curatorFramework.getData().forPath("/test"); log.info("get data from node :{} successfully.", new String(bytes)); } @Test public void testSetData() throws Exception { CuratorFramework curatorFramework = getCuratorFramework(); curatorFramework.setData().forPath("/test", "changed!".getBytes()); byte[] bytes = curatorFramework.getData().forPath("/test"); log.info("get data from node /curator-node :{} successfully.", new String(bytes)); } @Test public void testDelete() throws Exception { CuratorFramework curatorFramework = getCuratorFramework(); String pathWithParent = "/node-parent"; curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent); } @Test public void testListChildren() throws Exception { CuratorFramework curatorFramework = getCuratorFramework(); String pathWithParent = "/node-parent"; List<String> strings = curatorFramework.getChildren().forPath(pathWithParent); strings.forEach(System.out::println); } }
Asynchronous interface
Curator introduces the BackgroundCallback interface to handle the information returned from the server side, which is invoked in asynchronous threads. It is called by default in EventThread, or it can also customize the thread pool.
public interface BackgroundCallback { /** * Called when the async background operation completes * * @param client the client * @param event operation result details * @throws Exception errors */ public void processResult(CuratorFramework client, CuratorEvent event) throws Exception; }
For the above interface, the main parameters are client and server event events
inBackground asynchronous processing is executed in EventThread by default, and the thread pool is specified
@Test public void testThreadPool() throws Exception { CuratorFramework curatorFramework = getCuratorFramework(); ExecutorService executorService = Executors.newSingleThreadExecutor(); String ZK_NODE = "/zk-node"; curatorFramework.getData().inBackground((client, event) -> { log.info(" background: {}", event); }, executorService).forPath(ZK_NODE); }
Zoomeeper cluster & dynamic capacity expansion / reduction without shutdown
There are three types of roles in the zookeeper cluster pattern
- Leader: handles all transaction requests (write requests) and read requests. There can only be one leader in the cluster
- Follower: it can only process read requests and act as a candidate node for the Leader. That is, if the Leader goes down, the follower will participate in the new Leader election and may become a new Leader node.
- Observer: only read requests can be processed. Cannot participate in the election
zookeeper cluster setup
Configuration zoo1.cfg file
Configure zoo1.cfg, zoo2.cfg, zoo3.cfg and zoo4.cfg respectively
Start 4 zookeeper instances
Connection instance
bin/zkCli.sh -server 192.168.1.104:2181,192.168.1.104:2182,192.168.1.104:2183,192.168.1.104:2184
public static void main(String[] args) throws Exception { RetryPolicy retryPolicy=new ExponentialBackoffRetry( 5*1000, 10 ); String connectStr = "192.168.1.104:2181,192.168.1.104:2182,192.168.1.104:2183,192.168.1.104:2184"; CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectStr, retryPolicy); curatorFramework.start(); String pathWithParent = "/test"; byte[] bytes = curatorFramework.getData().forPath(pathWithParent); System.out.println(new String(bytes)); while (true) { try { byte[] bytes2 = curatorFramework.getData().forPath(pathWithParent); System.out.println(new String(bytes2)); TimeUnit.SECONDS.sleep(5); } catch (Exception e) { e.printStackTrace(); } } }