web-dev-qa-db-ja.com

Kafka:ZooKeeperからブローカーホストを取得する

特定の理由で、Kafkaから読み取るには、ConsumerGroup(高レベルコンシューマー)とSimpleConsumer(低レベルコンシューマー)の両方を使用する必要があります。 ConsumerGroupの場合、ZooKeeperベースの設定を使用し、これに完全に満足していますが、SimpleConsumerはシードブローカーをインスタンス化する必要があります。

ZooKeeperとブローカーホストの両方のリストを保持したくありません。したがって、特定のトピックについてブローカーを自動的に検出するZooKeeperからにする方法を探しています。

いくつかの間接情報Ibeliefにより、これらのデータは次のいずれかのパスでZooKeeperに保存されます。

  • /brokers/topics/<topic>/partitions/<partition-id>/state
  • / brokers/ids /

ただし、これらのノードからデータを読み取ろうとすると、シリアル化エラーが発生します(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オブジェクト(文字列など)を問題なく読み書きできるので、クライアントの問題ではなく、むしろ扱いにくいエンコーディングであると思います。したがって、私は知りたいです:

  1. これが正しい方法である場合、-これらのノードを適切に読み取る方法
  2. アプローチ全体が間違っている場合、正しいものは何ですか
26
ffriend

これが、同僚の一人が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}
30
Heejin

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")
  }
}
14
ffriend

シェルを使用してこれを行うには:

zookeeper-Shell myzookeeper.example.com:2181
ls /brokers/ids
  => [2, 1, 0]
get /brokers/ids/2
get /brokers/ids/1
get /brokers/ids/0 
8
phayes

実際には、Kafka(少なくとも0.8.x行の場合))内からZkUtilsがあり、1つの小さな警告で使用できます。ZkStringSerializerを再実装する必要があります文字列をUTF-8エンコードバイト配列として変換します。Java8のストリーミングAPIを使用する場合は、Scalaコレクションスルーscala.collection.JavaConversions。これが私のケースを助けたものです。

3
nefo_x
 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));
}
2
ljp