/**
* 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.
*/
package io.jafka.utils.zookeeper;
import com.github.zkclient.ZkClient;
import com.github.zkclient.exception.ZkNoNodeException;
import com.github.zkclient.exception.ZkNodeExistsException;
import io.jafka.cluster.Broker;
import io.jafka.cluster.Cluster;
import io.jafka.consumer.TopicCount;
import io.jafka.utils.Utils;
import java.util.*;
import static io.jafka.utils.Utils.fromBytes;
import static io.jafka.utils.Utils.getBytes;
/**
* @author adyliu (imxylz@gmail.com)
* @since 1.0
*/
public class ZkUtils {
public static final String ConsumersPath = "/consumers";
public static final String BrokerIdsPath = "/brokers/ids";
public static final String BrokerTopicsPath = "/brokers/topics";
public static void makeSurePersistentPathExists(ZkClient zkClient, String path) {
if (!zkClient.exists(path)) {
zkClient.createPersistent(path, true);
}
}
/**
* get children nodes name
*
* @param zkClient zkClient
* @param path full path
* @return children nodes name or null while path not exist
*/
public static List<String> getChildrenParentMayNotExist(ZkClient zkClient, String path) {
try {
return zkClient.getChildren(path);
} catch (ZkNoNodeException e) {
return null;
}
}
public static String readData(ZkClient zkClient, String path) {
return Utils.fromBytes(zkClient.readData(path));
}
public static String readDataMaybeNull(ZkClient zkClient, String path) {
return Utils.fromBytes(zkClient.readData(path, true));
}
public static void updatePersistentPath(ZkClient zkClient, String path, String data) {
try {
zkClient.writeData(path, Utils.getBytes(data));
} catch (ZkNoNodeException e) {
createParentPath(zkClient, path);
try {
zkClient.createPersistent(path, Utils.getBytes(data));
} catch (ZkNodeExistsException e2) {
zkClient.writeData(path, Utils.getBytes(data));
}
}
}
private static void createParentPath(ZkClient zkClient, String path) {
String parentDir = path.substring(0, path.lastIndexOf('/'));
if (parentDir.length() != 0) {
zkClient.createPersistent(parentDir, true);
}
}
/**
* read all brokers in the zookeeper
*
* @param zkClient zookeeper client
* @return all brokers
*/
public static Cluster getCluster(ZkClient zkClient) {
Cluster cluster = new Cluster();
List<String> nodes = getChildrenParentMayNotExist(zkClient, BrokerIdsPath);
for (String node : nodes) {
final String brokerInfoString = readData(zkClient, BrokerIdsPath + "/" + node);
cluster.add(Broker.createBroker(Integer.valueOf(node), brokerInfoString));
}
return cluster;
}
public static TopicCount getTopicCount(ZkClient zkClient, String group, String consumerId) {
ZkGroupDirs dirs = new ZkGroupDirs(group);
String topicCountJson = ZkUtils.readData(zkClient, dirs.consumerRegistryDir + "/" + consumerId);
return TopicCount.parse(consumerId, topicCountJson);
}
/**
* read broker info for watching topics
*
* @param zkClient the zookeeper client
* @param topics topic names
* @return topic->(brokerid-0,brokerid-1...brokerid2-0,brokerid2-1...)
*/
public static Map<String, List<String>> getPartitionsForTopics(ZkClient zkClient, Collection<String> topics) {
Map<String, List<String>> ret = new HashMap<String, List<String>>();
for (String topic : topics) {
List<String> partList = new ArrayList<String>();
List<String> brokers = getChildrenParentMayNotExist(zkClient, BrokerTopicsPath + "/" + topic);
if (brokers != null) {
for (String broker : brokers) {
final String parts = readData(zkClient, BrokerTopicsPath + "/" + topic + "/" + broker);
int nParts = Integer.parseInt(parts);
for (int i = 0; i < nParts; i++) {
partList.add(broker + "-" + i);
}
}
}
Collections.sort(partList);
ret.put(topic, partList);
}
return ret;
}
/**
* get all consumers for the group
*
* @param zkClient the zookeeper client
* @param group the group name
* @return topic->(consumerIdStringA-0,consumerIdStringA-1...consumerIdStringB-0,consumerIdStringB-1)
*/
public static Map<String, List<String>> getConsumersPerTopic(ZkClient zkClient, String group) {
ZkGroupDirs dirs = new ZkGroupDirs(group);
List<String> consumers = getChildrenParentMayNotExist(zkClient, dirs.consumerRegistryDir);
//
Map<String, List<String>> consumersPerTopicMap = new HashMap<String, List<String>>();
for (String consumer : consumers) {
TopicCount topicCount = getTopicCount(zkClient, group, consumer);
for (Map.Entry<String, Set<String>> e : topicCount.getConsumerThreadIdsPerTopic().entrySet()) {
final String topic = e.getKey();
for (String consumerThreadId : e.getValue()) {
List<String> list = consumersPerTopicMap.get(topic);
if (list == null) {
list = new ArrayList<String>();
consumersPerTopicMap.put(topic, list);
}
//
list.add(consumerThreadId);
}
}
}
//
for (Map.Entry<String, List<String>> e : consumersPerTopicMap.entrySet()) {
Collections.sort(e.getValue());
}
return consumersPerTopicMap;
}
//
public static void deletePath(ZkClient zkClient, String path) {
try {
zkClient.delete(path);
} catch (ZkNoNodeException e) {
}
}
public static String readDataMaybyNull(ZkClient zkClient, String path) {
return Utils.fromBytes(zkClient.readData(path, true));
}
/**
* Create an ephemeral node with the given path and data. Create parents if necessary.
* @param zkClient client of zookeeper
* @param path node path of zookeeper
* @param data node data
*/
public static void createEphemeralPath(ZkClient zkClient, String path, String data) {
try {
zkClient.createEphemeral(path, Utils.getBytes(data));
} catch (ZkNoNodeException e) {
createParentPath(zkClient, path);
zkClient.createEphemeral(path, Utils.getBytes(data));
}
}
public static void createEphemeralPathExpectConflict(ZkClient zkClient, String path, String data) {
try {
createEphemeralPath(zkClient, path, data);
} catch (ZkNodeExistsException e) {
//this can happend when there is connection loss;
//make sure the data is what we intend to write
String storedData = null;
try {
storedData = readData(zkClient, path);
} catch (ZkNoNodeException e2) {
//ignore
}
if (storedData == null || !storedData.equals(data)) {
throw new ZkNodeExistsException("conflict in " + path + " data: " + data + " stored data: " + storedData);
}
//
//otherwise, the creation succeeded, return normally
}
}
}