特定の理由で、Kafkaから読み取るには、ConsumerGroup
(高レベルコンシューマー)とSimpleConsumer
(低レベルコンシューマー)の両方を使用する必要があります。 ConsumerGroup
の場合、ZooKeeperベースの設定を使用し、これに完全に満足していますが、SimpleConsumer
はシードブローカーをインスタンス化する必要があります。
ZooKeeperとブローカーホストの両方のリストを保持したくありません。したがって、特定のトピックについてブローカーを自動的に検出するZooKeeperからにする方法を探しています。
いくつかの間接情報Ibeliefにより、これらのデータは次のいずれかのパスでZooKeeperに保存されます。
/brokers/topics/<topic>/partitions/<partition-id>/state
ただし、これらのノードからデータを読み取ろうとすると、シリアル化エラーが発生します(com.101tec.zkclient
このため):
org.I0Itec.zkclient.exception.ZkMarshallingError:Java.io.StreamCorruptedException:invalid stream header:7B226A6D at org.I0Itec.zkclient.serialize.SerializableSerializer.deserialize(SerializableSerializer.Java:37)at org.I0Itec.zkclient.ZkClient.ZkClient.ZkClient.ZkClient。 (ZkClient.Java:740)org.I0Itec.zkclient.ZkClient.readData(ZkClient.Java:773)at org.I0Itec.zkclient.ZkClient.readData(ZkClient.Java:761)at org.I0Itec.zkclient.ZkClient。 readData(ZkClient.Java:750)at org.I0Itec.zkclient.ZkClient.readData(ZkClient.Java:744)... 64省略原因:Java.io.StreamCorruptedException:無効なストリームヘッダー:Java.io.ObjectInputStreamの7B226A6D org.I0Itec.zkclient.serialize.TcclAwareObjectIputStream。(TcclAwareObjectIputStream.Java:30)のJava.io.ObjectInputStream。(ObjectInputStream.Java:299)の.readStreamHeader(ObjectInputStream.Java:804)org.I0Itec.zkclient.serializeのSerializableSerializer.deserialize(SerializableSerializer.Java:31)... 69その他
私はカスタムJavaオブジェクト(文字列など)を問題なく読み書きできるので、クライアントの問題ではなく、むしろ扱いにくいエンコーディングであると思います。したがって、私は知りたいです:
これが、同僚の一人がKafka=ブローカーのリストを取得するために行った方法です。ブローカーリストを動的に取得する場合は正しい方法だと思います。
リストを取得する方法を示すサンプルコードを次に示します。
public class KafkaBrokerInfoFetcher {
public static void main(String[] args) throws Exception {
ZooKeeper zk = new ZooKeeper("localhost:2181", 10000, null);
List<String> ids = zk.getChildren("/brokers/ids", false);
for (String id : ids) {
String brokerInfo = new String(zk.getData("/brokers/ids/" + id, false, null));
System.out.println(id + ": " + brokerInfo);
}
}
}
3つのブローカーで構成されるクラスターでコードを実行すると、
1: {"jmx_port":-1,"timestamp":"1428512949385","Host":"192.168.0.11","version":1,"port":9093}
2: {"jmx_port":-1,"timestamp":"1428512955512","Host":"192.168.0.11","version":1,"port":9094}
3: {"jmx_port":-1,"timestamp":"1428512961043","Host":"192.168.0.11","version":1,"port":9095}
KafkaはZKStringSerializer
を使用してznodeにデータを読み書きします。したがって、エラーを修正するには、ZkClient
コンストラクター:
val zkClient = new ZkClient(zkQuorum, Integer.MAX_VALUE, 10000, ZKStringSerializer)
それを使用して、ブローカーID、そのアドレス、およびその他のものを検出するためのいくつかの便利な関数を作成しました。
import kafka.utils.Json
import kafka.utils.ZKStringSerializer
import kafka.utils.ZkUtils
import org.I0Itec.zkclient.ZkClient
import org.Apache.kafka.common.KafkaException
def listBrokers(): List[Int] = {
zkClient.getChildren("/brokers/ids").toList.map(_.toInt)
}
def listTopics(): List[String] = {
zkClient.getChildren("/brokers/topics").toList
}
def listPartitions(topic: String): List[Int] = {
val path = "/brokers/topics/" + topic + "/partitions"
if (zkClient.exists(path)) {
zkClient.getChildren(path).toList.map(_.toInt)
} else {
throw new KafkaException(s"Topic ${topic} doesn't exist")
}
}
def getBrokerAddress(brokerId: Int): (String, Int) = {
val path = s"/brokers/ids/${brokerId}"
if (zkClient.exists(path)) {
val brokerInfo = readZkData(path)
(brokerInfo.get("Host").get.asInstanceOf[String], brokerInfo.get("port").get.asInstanceOf[Int])
} else {
throw new KafkaException("Broker with ID ${brokerId} doesn't exist")
}
}
def getLeaderAddress(topic: String, partitionId: Int): (String, Int) = {
val path = s"/brokers/topics/${topic}/partitions/${partitionId}/state"
if (zkClient.exists(path)) {
val leaderStr = zkClient.readData[String](path)
val leaderId = Json.parseFull(leaderStr).get.asInstanceOf[Map[String, Any]].get("leader").get.asInstanceOf[Int]
getBrokerAddress(leaderId)
} else {
throw new KafkaException(s"Topic (${topic}) or partition (${partitionId}) doesn't exist")
}
}
シェルを使用してこれを行うには:
zookeeper-Shell myzookeeper.example.com:2181
ls /brokers/ids
=> [2, 1, 0]
get /brokers/ids/2
get /brokers/ids/1
get /brokers/ids/0
実際には、Kafka(少なくとも0.8.x行の場合))内からZkUtils
があり、1つの小さな警告で使用できます。ZkStringSerializerを再実装する必要があります文字列をUTF-8エンコードバイト配列として変換します。Java8のストリーミングAPIを使用する場合は、Scalaコレクションスルーscala.collection.JavaConversions
。これが私のケースを助けたものです。
public KafkaProducer(String zookeeperAddress, String topic) throws IOException,
KeeperException, InterruptedException {
this.zookeeperAddress = zookeeperAddress;
this.topic = topic;
ZooKeeper zk = new ZooKeeper(zookeeperAddress, 10000, null);
List<String> brokerList = new ArrayList<String>();
List<String> ids = zk.getChildren("/brokers/ids", false);
for (String id : ids) {
String brokerInfoString = new String(zk.getData("/brokers/ids/" + id, false, null));
Broker broker = Broker.createBroker(Integer.valueOf(id), brokerInfoString);
if (broker != null) {
brokerList.add(broker.connectionString());
}
}
props.put("serializer.class", KAFKA_STRING_ENCODER);
props.put("metadata.broker.list", String.join(",", brokerList));
producer = new Producer<String, String>(new ProducerConfig(props));
}