[Kafka] Cómo Kafka implementa la supervisión a través del código fuente

Inserte la descripción de la imagen aquí

1. Información general

Guía de preguntas:

1. Almacenamiento compensado del consumidor del grupo de consumidores de
Kafka , Kafka admite dos versiones 2. ¿Cuál es el papel de la clase ConsumerOffsetChecker?
3. ¿Cómo realiza Kafka el seguimiento a través del código fuente?

1. Introducción a las ideas básicas

Como cola de mensajes útil y ampliamente utilizada, Kafka es básicamente indispensable en los sistemas de procesamiento de big data. Por supuesto, como cola de mensajes para almacenar mensajes en caché, es extremadamente importante para nosotros monitorear su tráfico y alertar sobre el retraso de consumo.

Los hermanos y hermanas que hayan leído los artículos anteriores, <Kafka fuente de la serie de código fuente de análisis de código proceso de consumo SimpleConsumer> y <Kafka código fuente de la serie de consumidor avanzado análisis de rendimiento API> deben leer este artículo será muy simple. De hecho, utiliza SimpleConsumer para obtener el último desplazamiento de Partition y utiliza la herramienta de Zookeeper para obtener el desplazamiento de consumo de cada partición del grupo de consumidores, la diferencia entre los dos es lagSize.
Pero el almacenamiento de compensación de consumo real del grupo de consumidores de Kafka, Kafka admite dos versiones:

1,基于Zookeeper。OffsetFetchRequest.CurrentVersion为02,基于kafka自身。OffsetFetchRequest.CurrentVersion为1(默认)

Para darse cuenta de la advertencia de retraso de un consumidor, dos métodos deben ser compatibles, luego presentaremos la realización de estos dos métodos en detalle.

Segundo, herramientas importantes

1,ConsumerOffsetChecker

Kafka proporciona herramientas para verificar la compensación del consumo del consumidor, LogEndSize y lagsize. Podemos imitar este tipo de implementación cuando implementamos nuestro propio monitoreo. Este artículo también se limita al proceso de implementación basado en esta clase.

2 , ZkUtils

Kafka proporciona herramientas para operar Zookeeper.

3,SimpleConsumer

Clase de implementación de consumidor de Kafka. La sincronización de réplicas de Kafka, los consumidores de bajo nivel y los consumidores de alto nivel se basan en este tipo de implementación para consumir mensajes de Kafka.

4, OffsetRequest

El consumidor obtiene la clase de solicitud del desplazamiento de datos de la partición y la clave de solicitud correspondiente es: RequestKeys.OffsetsKey. La función de procesamiento de kafkaApis en el lado del servidor de kafka es: handleOffsetRequest (solicitud)

5, OffsetFetchRequest

Esta es la compensación de consumo de un determinado grupo de consumidores que solicita un determinado tema y la clave de solicitud correspondiente: RequestKeys.OffsetFetchKey. La función de procesamiento de kafkaApis en el lado del servidor de kafka es: handleOffsetFetchRequest (solicitud)

6 , OffsetManager

Administrador de compensaciones. Un planificador se mantiene internamente y compacto se ejecuta regularmente para fusionar compensaciones.

Tres, implementación del código fuente

1. El primero es obtener la compensación del consumidor
ConsumerOffsetChecker cuando el método principal es el primero en obtener la lista de temas.

val topicList = topics match {
    
    
  case Some(x) => x.split(",").view.toList
  case None => ZkUtils.getChildren(zkClient, groupDirs.consumerGroupDir +  "/owners").toList
}

Lo siguiente es establecer un enlace a Broker y luego obtener la compensación del consumidor de kafka

val topicPartitions = topicPidMap.flatMap {
    
     case(topic, partitionSeq) => partitionSeq.map(TopicAndPartition(topic, _)) }.toSeq
val channel = ClientUtils.channelToOffsetManager(group, zkClient, channelSocketTimeoutMs, channelRetryBackoffMs)
 
debug("Sending offset fetch request to coordinator %s:%d.".format(channel.host, channel.port))
channel.send(OffsetFetchRequest(group, topicPartitions))
val offsetFetchResponse = OffsetFetchResponse.readFrom(channel.receive().buffer)
debug("Received offset fetch response %s.".format(offsetFetchResponse))
 
offsetFetchResponse.requestInfo.foreach {
    
     case (topicAndPartition, offsetAndMetadata) =>
  if (offsetAndMetadata == OffsetMetadataAndError.NoOffset) {
    
    
    val topicDirs = new ZKGroupTopicDirs(group, topicAndPartition.topic)
    // this group may not have migrated off zookeeper for offsets storage (we don't expose the dual-commit option in this tool
    // (meaning the lag may be off until all the consumers in the group have the same setting for offsets storage)
    try {
    
    
      val offset = ZkUtils.readData(zkClient, topicDirs.consumerOffsetDir + "/%d".format(topicAndPartition.partition))._1.toLong
      offsetMap.put(topicAndPartition, offset)
    } catch {
    
    
      case z: ZkNoNodeException =>
        if(ZkUtils.pathExists(zkClient,topicDirs.consumerOffsetDir))
          offsetMap.put(topicAndPartition,-1)
        else
          throw z
    }
  }
  else if (offsetAndMetadata.error == ErrorMapping.NoError)
    offsetMap.put(topicAndPartition, offsetAndMetadata.offset)
  else {
    
    
    println("Could not fetch offset for %s due to %s.".format(topicAndPartition, ErrorMapping.exceptionFor(offsetAndMetadata.error)))
  }
}

Si la información de compensación obtenida está vacía, la compensación del consumidor se obtiene de Zookeeper.
Para resolver el desplazamiento máximo de la partición del tema, la idea real es construir un simpleConsumer, luego solicitar el desplazamiento y luego hacer una diferencia con el desplazamiento del consumidor obtenido para obtener el desplazamiento máximo del consumidor.

topicList.sorted.foreach {
    
    
  topic => processTopic(zkClient, group, topic)
}
topicPidMap.get(topic) match {
    
    
  case Some(pids) =>
    pids.sorted.foreach {
    
    
      pid => processPartition(zkClient, group, topic, pid)
    }
  case None => // ignore
}

En proceso Partición

val offsetOpt = offsetMap.get(topicPartition)
val groupDirs = new ZKGroupTopicDirs(group, topic)
val owner = ZkUtils.readDataMaybeNull(zkClient, groupDirs.consumerOwnerDir + "/%s".format(pid))._1
ZkUtils.getLeaderForPartition(zkClient, topic, pid) match {
    
    
  case Some(bid) =>
    val consumerOpt = consumerMap.getOrElseUpdate(bid, getConsumer(zkClient, bid))
    consumerOpt match {
    
    
      case Some(consumer) =>
        val topicAndPartition = TopicAndPartition(topic, pid)
        val request =
          OffsetRequest(immutable.Map(topicAndPartition -> PartitionOffsetRequestInfo(OffsetRequest.LatestTime, 1)))
        val logSize = consumer.getOffsetsBefore(request).partitionErrorAndOffsets(topicAndPartition).offsets.head

Luego haz la diferencia para obtener LagSize

val lagString = offsetOpt.map(o => if (o == -1) "unknown" else (logSize - o).toString)
println("%-15s %-30s %-3s %-15s %-15s %-15s %s".format(group, topic, pid, offsetOpt.getOrElse("unknown"), logSize, lagString.getOrElse("unknown"),
                                                       owner match {
    
    case Some(ownerStr) => ownerStr case None => "none"}))

En el método getConsumer

private def getConsumer(zkClient: ZkClient, bid: Int): Option[SimpleConsumer] = {
    
    
  try {
    
    
    ZkUtils.readDataMaybeNull(zkClient, ZkUtils.BrokerIdsPath + "/" + bid)._1 match {
    
    
      case Some(brokerInfoString) =>
        Json.parseFull(brokerInfoString) match {
    
    
          case Some(m) =>
            val brokerInfo = m.asInstanceOf[Map[String, Any]]
            val host = brokerInfo.get("host").get.asInstanceOf[String]
            val port = brokerInfo.get("port").get.asInstanceOf[Int]
            Some(new SimpleConsumer(host, port, 10000, 100000, "ConsumerOffsetChecker"))
          case None =>
            throw new BrokerNotAvailableException("Broker id %d does not exist".format(bid))
        }
      case None =>
        throw new BrokerNotAvailableException("Broker id %d does not exist".format(bid))
    }
  } catch {
    
    
    case t: Throwable =>
      println("Could not parse broker info due to " + t.getCause)
      None
  }
}

Cuatro, resumen

Uso de esta herramienta

bin/kafka-consumer-offset-checker.sh --group yourgroup -topic yourtopic --zookeeper  localhost:2181

Resultado de salida

La compensación es la compensación consumida por los consumidores, el tamaño de registro es la compensación máxima de los datos de Kafka y el Lag es la diferencia entre los dos. Es decir

LagSize = LogSize - Offset

Después de obtener la histéresis de nuestro grupo de consumidores, podemos dar las alarmas correspondientes según la demanda (por ejemplo, establecer el número de mensajes retrasados ​​para dar una alarma).

Supongo que te gusta

Origin blog.csdn.net/qq_21383435/article/details/111120137
Recomendado
Clasificación