web-dev-qa-db-fra.com

Kafka: Obtenez l'hôte de courtier de ZooKeeper

Pour des raisons particulières, je dois utiliser à la fois - ConsumerGroup (a.k.a. consommateur de haut niveau) et SimpleConsumer (a.k.a. consommateur de bas niveau) pour lire à partir de Kafka. Pour ConsumerGroup j'utilise la configuration basée sur ZooKeeper et j'en suis entièrement satisfait, mais SimpleConsumer nécessite que les courtiers de semences soient instanciés.

Je ne veux pas garder la liste des deux - ZooKeeper et les courtiers hôtes. Ainsi, je cherche un moyen de découvrir automatiquement les courtiers pour un sujet particulier de ZooKeeper.

En raison de certaines informations indirectes, je pense que ces données sont stockées dans ZooKeeper sous l'un des chemins suivants:

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

Cependant, lorsque j'essaie de lire les données de ces nœuds, j'obtiens une erreur de sérialisation (j'utilise com.101tec.zkclient pour ça):

org.I0Itec.zkclient.exception.ZkMarshallingError: Java.io.StreamCorruptedException: en-tête de flux non valide: 7B226A6D à org.I0Itec.zkclient.serialize.SerializableSerializer.deserialize (SerializableSerializer.Java:37) à org.I0ItientClient (ZkClient.Java:740) à org.I0Itec.zkclient.ZkClient.readData (ZkClient.Java:773) à org.I0Itec.zkclient.ZkClient.readData (ZkClient.Java:761) à org.I0Itec.zkclient.ZkClient. readData (ZkClient.Java:750) à org.I0Itec.zkclient.ZkClient.readData (ZkClient.Java:744) ... 64 élidé Causé par: Java.io.StreamCorruptedException: en-tête de flux non valide: 7B226A6D à Java.io.ObjectInputStream .readStreamHeader (ObjectInputStream.Java:804) sur Java.io.ObjectInputStream. (ObjectInputStream.Java:299) sur org.I0Itec.zkclient.serialize.TcclAwareObjectIputStream. (TcclAwareObjectIputStream. (TcclAwareObjectIputStream. SerializableSerializer.deserialize (SerializableSerializer.Java:31) ... 69 plus

Je peux écrire et lire des objets Java Java (par exemple Strings) personnalisés sans aucun problème, donc je pense que ce n'est pas un problème de client, mais un encodage plutôt délicat. Je veux donc savoir:

  1. Si c'est la bonne façon de procéder, comment lire correctement ces nœuds?
  2. Si toute l'approche est fausse, quelle est la bonne?
26
ffriend

C'est la façon dont un de mes collègues a fait pour obtenir une liste de courtiers Kafka. Je pense que c'est une façon correcte lorsque vous souhaitez obtenir une liste de courtiers de manière dynamique.

Voici un exemple de code qui montre comment obtenir la liste.

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);
        }
    }
}

L'exécution du code sur le cluster composé de trois courtiers entraîne

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

Il s'avère que Kafka utilise ZKStringSerializer pour lire et écrire des données dans des znodes. Donc, pour corriger l'erreur, je n'ai eu qu'à l'ajouter comme dernier paramètre dans ZkClient constructeur:

val zkClient = new ZkClient(zkQuorum, Integer.MAX_VALUE, 10000, ZKStringSerializer)

En l'utilisant, j'ai écrit plusieurs fonctions utiles pour découvrir les identifiants des courtiers, leurs adresses et d'autres choses:

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

Pour ce faire à l'aide du Shell:

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

en fait, il y a ZkUtils de l'intérieur Kafka (au moins pour la ligne 0.8.x), que vous pouvez utiliser avec une petite mise en garde: vous devrez réimplémenter ZkStringSerializer qui convertirait des chaînes en tableaux d'octets encodés en UTF-8. Si vous souhaitez utiliser les API de streaming de Java8, vous pouvez itérer sur Scala collections throug scala.collection.JavaConversions. C'est la chose qui a aidé mon cas.

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