Análisis del principio de Kafka y la estrategia de asignación de particiones

1. Introducción

 Apache Kafka es una plataforma de procesamiento de flujo distribuido (una cola de mensajes distribuida [Message Queue] basada en el modelo de publicación / suscripción).

La plataforma de procesamiento de flujo tiene las siguientes 3 características:

  • Le permite publicar y suscribirse a registros de transmisión. Este aspecto es similar a las colas de mensajes o los sistemas de mensajería empresarial.

  • Puede almacenar registros de transmisión y tiene una mejor tolerancia a fallas.

  • Se puede procesar tan pronto como se genere el registro de transmisión.

1.1 Dos modos de cola de mensajes

1.1.1 Modo punto a punto

El productor envía el mensaje a la cola y luego el consumidor lo saca de la cola y consume el mensaje. Una vez consumido el mensaje, ya no se almacena en la cola, por lo que es imposible que el consumidor consuma el mensaje consumido. Queue admite varios consumidores, pero para un mensaje, solo puede ser consumido por un consumidor.

Análisis del principio de Kafka y la estrategia de asignación de particiones

1.1.2 Modelo de publicación / suscripción

El productor publica el mensaje sobre el tema y varios consumidores pueden suscribirse al mensaje al mismo tiempo. A diferencia del método peer-to-peer, todos los suscriptores consumirán los mensajes publicados en el tema.

Análisis del principio de Kafka y la estrategia de asignación de particiones

1.2 ¿Para qué tipo de escenario es adecuado Kafka?

Se puede utilizar en dos categorías de aplicaciones:

  • Construya una canalización de datos de transmisión en tiempo real, que pueda obtener datos de manera confiable entre sistemas o aplicaciones. (Equivalente a la cola de mensajes).

  • Cree aplicaciones de transmisión en tiempo real para transformar o influir en estos datos de transmisión. (Es el procesamiento de la secuencia, a través de cambios internos entre el tema de la secuencia de Kafka y el tema).

Para comprender cómo Kafka logra las funciones antes mencionadas, a partir de lo siguiente, exploraremos las características de Kafka en profundidad.

Primero, algunos conceptos:

  • Como clúster, Kafka se ejecuta en uno o más servidores.

  • Kafka clasifica los datos de transmisión almacenados por tema.

  • Cada registro contiene una clave, un valor y una marca de tiempo (marca de tiempo).

1.3 temas y divisiones

Los mensajes de Kafka se clasifican por temas, que son como tablas o carpetas de bases de datos en el sistema de archivos. El tema se puede dividir en varias particiones (Partición), una partición es un registro de confirmación. Los mensajes se escriben en la partición en anexo y luego se leen en el orden de primero en entrar, primero en salir. Tenga en cuenta que, dado que un tema generalmente contiene varias particiones, no se puede garantizar el orden de los mensajes dentro de todo el tema, pero sí se puede garantizar el orden de los mensajes dentro de una sola partición. El tema es un concepto lógico y, físicamente, un tema abarca varios servidores.

Análisis del principio de Kafka y la estrategia de asignación de particiones

El clúster de Kafka conserva todos los registros publicados (independientemente de si se han consumido) y está controlado por un período de retención de parámetros configurable (el tiempo y el tamaño del mensaje se pueden configurar al mismo tiempo, el que sea menor). Por ejemplo, si la política de retención se establece en 2 días, un registro se puede consumir en cualquier momento dentro de los dos días posteriores a su publicación. Después de dos días, el registro se descartará y se liberará el espacio en disco.

A veces necesitamos aumentar la cantidad de particiones, como para expandir la capacidad del tema, reducir el rendimiento de una sola partición o ejecutar más consumidores en un solo grupo de consumidores (porque una partición solo puede ser consumida por un consumidor en el grupo de consumidores Lector). Desde el punto de vista del consumidor, es muy difícil agregar particiones basadas en temas clave, porque la cantidad de particiones cambia, la asignación de claves a particiones también cambiará, por lo que para temas basados ​​en claves, se recomienda configurar particiones al principio. Para evitar ajustarlo más tarde.

(Nota: el número de particiones no se puede reducir, porque si se elimina la partición, los datos de la partición también se eliminan, lo que da como resultado una inconsistencia de datos. Si debe reducir el número de particiones, solo puede eliminar la reconstrucción del tema)

1.4 Productores y consumidores

El productor (editor) crea un mensaje. En general, se publicará un mensaje sobre un tema específico. El productor distribuye el mensaje de manera uniforme a todas las particiones del tema de forma predeterminada y no le importa en qué partición se escribirá un mensaje en particular. Sin embargo, el productor también puede escribir el mensaje directamente en la partición designada. Por lo general, esto se logra mediante una clave de mensaje y un particionador, que genera un valor hash para la clave y lo asigna a la partición especificada. Los productores también pueden personalizar el particionador para asignar mensajes a particiones de acuerdo con diferentes reglas comerciales.

Los consumidores (suscriptores) leen mensajes. Los consumidores pueden suscribirse a uno o más temas y leerlos en el orden en que se generan los mensajes. Los consumidores distinguen los mensajes leídos comprobando el desplazamiento del mensaje. El desplazamiento es una especie de metadatos, es un valor entero creciente, al crear un mensaje, Kafka lo agregará al mensaje. En una partición determinada, el desplazamiento de cada mensaje es único. El consumidor guarda el último desplazamiento de mensaje leído de cada partición en zookeeper o kafka. Si el consumidor se apaga o reinicia, su estado de lectura no se perderá.

Los consumidores forman parte de un grupo de consumidores, es decir, uno o más consumidores leerán un tema juntos. El grupo de consumidores garantiza que cada partición solo puede ser utilizada por un consumidor del mismo grupo. Si un consumidor falla, otros consumidores del grupo pueden hacerse cargo del trabajo del consumidor discapacitado.

Análisis del principio de Kafka y la estrategia de asignación de particiones

1.5 corredor y clúster

broker: un servidor Kafka independiente se denomina broker. El corredor recibe el mensaje del productor, establece el desplazamiento para el mensaje y envía el mensaje al disco para su almacenamiento. El corredor brinda servicios a los consumidores, responde a las solicitudes de lectura de particiones y devuelve los mensajes que se han enviado al disco.

Clúster: los nodos de intermediarios se entregan al mismo clúster del guardián del zoológico para que los administren desde un clúster de Kafka.

El intermediario es un componente del clúster y cada clúster tiene un intermediario que también actúa como controlador de clúster. El controlador es responsable de la gestión, incluida la asignación de particiones a los corredores y los corredores de supervisión. En un corredor, una partición pertenece a un corredor y el corredor se denomina líder de la partición. Se puede asignar una partición a varios intermediarios (cuando se establecen varias copias del tema), luego se producirá la replicación de la partición. Como se muestra abajo:

Análisis del principio de Kafka y la estrategia de asignación de particiones

Cómo maneja el intermediario la solicitud: El intermediario ejecuta un hilo de aceptación en cada puerto que escucha. Este hilo crea una conexión y la entrega al hilo del procesador para su procesamiento. El número de subprocesos del procesador (también llamados subprocesos de red) es configurable. El subproceso del procesador es responsable de obtener la información de la solicitud del cliente, colocarla en la cola de solicitudes y luego obtener la información de respuesta de la cola de respuestas y enviarla al cliente. Como se muestra abajo:

Análisis del principio de Kafka y la estrategia de asignación de particiones

Tanto la solicitud de producción como la solicitud de adquisición deben enviarse a la copia líder de la partición (el líder de la partición). Si el intermediario recibe una solicitud para una partición en particular, y el líder de la partición está en otro intermediario, el cliente que envía la solicitud recibirá una respuesta de error "no líder de partición". Los clientes de Kafka son responsables de enviar solicitudes de producción y solicitudes de adquisición al corredor correcto.

¿Cómo sabe el cliente dónde enviar la solicitud? El cliente utiliza otra solicitud de metadatos de tipo de solicitud. Esta solicitud contiene una lista de temas que le interesan al cliente. El mensaje de respuesta del servidor especifica las particiones incluidas en estos temas, qué copias de cada partición tienen y qué copia es el líder. Las solicitudes de metadatos se pueden enviar a cualquier corredor, porque todos los intermediarios almacenan esta información en caché. El cliente almacena en caché estos metadatos y periódicamente solicita al corredor que actualice esta información. Además, si el cliente recibe un error "no líder", actualizará los metadatos antes de intentar reenviar la solicitud.

Análisis del principio de Kafka y la estrategia de asignación de particiones

1.6 Infraestructura de Kafka

Análisis del principio de Kafka y la estrategia de asignación de particiones

 En segundo lugar, la arquitectura de Kafka es profunda

2.1 Flujo de trabajo de Kafka y mecanismo de almacenamiento de archivos

2.1.1 Flujo de trabajo

Análisis del principio de Kafka y la estrategia de asignación de particiones

Los mensajes en Kafka se clasifican por tema. Los productores producen mensajes y los consumidores consumen mensajes, todos ellos orientados a temas.

El tema es un concepto lógico y la partición (partición) es un concepto físico.Cada partición corresponde a un archivo de registro y el archivo de registro almacena los datos producidos por el productor. Los datos producidos por el productor se agregarán continuamente al final del archivo de registro, y cada dato tiene su propio desplazamiento. Cada consumidor del grupo de consumidores registrará en tiempo real qué offset consume, de modo que cuando se restablezca el error, continuará consumiendo desde la última posición.

2.1.2 Mecanismo de almacenamiento de archivos

Análisis del principio de Kafka y la estrategia de asignación de particiones

Dado que los mensajes producidos por el productor se añaden constantemente al final del archivo de registro, para evitar que el archivo de registro excesivamente grande cause un posicionamiento de datos ineficiente, Kafka adopta un mecanismo de fragmentación e indexación y divide cada partición en varios segmentos. (Determinado por log.segment.bytes, controle el tamaño de cada segmento, o controle por log.segment.ms , especifique cuánto tiempo después de que se cerrará el segmento de registro) Cada segmento corresponde a dos archivos - ". Index" Archivos y archivos ".log". Estos archivos se encuentran en una carpeta y la regla de nomenclatura de la carpeta es: nombre del tema + número de partición. Por ejemplo, el tema bing tiene 3 particiones y las carpetas correspondientes son: bing-0, bing-1 y bing-2.

 Reglas de nomenclatura de archivo de índice y archivo de registro: cada segmento de registro tiene un desplazamiento base, que se utiliza para representar el desplazamiento del primer mensaje en el segmento de registro actual. El desplazamiento es un entero de 64 bits, que se fija en 20 dígitos. Si no se alcanza la longitud, se rellena con 0. Como se muestra abajo:

Análisis del principio de Kafka y la estrategia de asignación de particiones

Los archivos de índice y registro reciben el nombre del desplazamiento del primer mensaje del segmento actual. El archivo de índice registra el desplazamiento y la ubicación física correspondiente del archivo de datos. Es este archivo de índice el que puede escribir y ver cualquier dato con complejidad O (1). La granularidad del archivo de índice se puede pasar a través del registro de parámetros. Index.interval.bytes para controlar, el valor predeterminado es registrar un índice cada 4096 bytes. La siguiente figura muestra el diagrama de estructura del archivo de índice y el archivo de registro:

Análisis del principio de Kafka y la estrategia de asignación de particiones

El proceso de encontrar el mensaje (por ejemplo, para encontrar el mensaje cuyo desplazamiento es 170417):

  1. Primero use la búsqueda binaria para determinar en qué archivo de segmento se encuentra, donde 0000000000000000000.index es el primer archivo, el segundo archivo es 0000000000000170410.index (el desplazamiento inicial es 170410 + 1 = 170411) y el tercer archivo es 0000000000000239430.index (el desplazamiento inicial es 239430 + 1 = 239431). Entonces este desplazamiento = 170417 cae en el segundo archivo. Otros archivos posteriores se pueden deducir de la misma manera, nombrar y organizar por el desplazamiento inicial, y luego pueden ubicar rápidamente la ubicación específica del archivo de acuerdo con el método de búsqueda binaria.

  2. Use el desplazamiento para restar el número del archivo de índice, es decir, 170417-170410 = 7, y use el método de búsqueda binaria para encontrar el número más grande en el archivo de índice que sea igual o menor que 7. Puede verse que podemos encontrar el conjunto de datos [4,476], 476 es el desplazamiento del mensaje con desplazamiento = 170410 + 4 = 170414 en el archivo de registro.

  3. Abra el archivo de datos (0000000000000170410.log) y escanee secuencialmente desde el lugar en la posición 476 hasta que se encuentre el mensaje con el desplazamiento 170417.

2.1.3 Mecanismo de caducidad de datos

Cuando el tamaño del segmento de registro alcanza el límite superior especificado por log.segment.bytes (el valor predeterminado es 1 GB) o el tiempo de apertura del segmento de registro alcanza log.segment.ms , el segmento de registro actual se cerrará y se abrirá un nuevo segmento de registro. Si se cierra un fragmento de registro, comienza a esperar a que caduque. El fragmento que se está escribiendo actualmente se llama fragmento activo, y el fragmento activo nunca se eliminará, por lo que si desea conservar los datos durante 1 día, pero el fragmento contiene datos durante 5 días, estos datos se conservarán durante 5 días porque antes de que se cierre el fragmento , Estos datos no se pueden eliminar.

2.2 productor de Kafka

2.2.1 Estrategia de partición

  1. El almacenamiento distribuido de múltiples particiones favorece el equilibrio de los datos del clúster.

  2. Lectura y escritura concurrentes para acelerar la lectura y escritura.

  3. Acelere la tasa de recuperación de datos: cuando una máquina se cuelga, cada tema solo necesita restaurar una parte de los datos y varias máquinas son simultáneas.

Principio de zonificación

  1. En el caso de especificar la partición, use la partición especificada;

  2. No se especifica ninguna partición, pero si hay una clave, el valor hash de la clave y el número de partición del tema se calculan para obtener el valor de la partición;

  3. Cuando no se especifica ni una partición ni una clave, se genera un número entero aleatoriamente durante la primera llamada (incremento en este número entero para cada llamada subsiguiente), y el valor de la partición se obtiene tomando el resto de este valor y el número de particiones disponibles para el tema. Es el algoritmo a menudo llamado round-robin.

public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
    List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
    int numPartitions = partitions.size();
    if (keyBytes == null) {
        //key为空时,获取一个自增的计数,然后对分区做取模得到分区编号
        int nextValue = nextValue(topic);
        List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
        if (availablePartitions.size() > 0) {
            int part = Utils.toPositive(nextValue) % availablePartitions.size();
            return availablePartitions.get(part).partition();
        } else {
            // no partitions are available, give a non-available partition
            return Utils.toPositive(nextValue) % numPartitions;
        }
    } else {
        // hash the keyBytes to choose a partition
        // key不为空时,通过key的hash对分区取模(疑问:为什么这里不像上面那样,使用availablePartitions呢?)
        // 根据《Kafka权威指南》Page45理解:为了保证相同的键,总是能路由到固定的分区,如果使用可用分区,那么因为分区数变化,会导致相同的key,路由到不同分区
        // 所以如果要使用key来映射分区,最好在创建主题的时候就把分区规划好
        return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
    }
}

private int nextValue(String topic) {
    //为每个topic维护了一个AtomicInteger对象,每次获取时+1
    AtomicInteger counter = topicCounterMap.get(topic);
    if (null == counter) {
        counter = new AtomicInteger(ThreadLocalRandom.current().nextInt());
        AtomicInteger currentCounter = topicCounterMap.putIfAbsent(topic, counter);
        if (currentCounter != null) {
            counter = currentCounter;
        }
    }
    return counter.getAndIncrement();
}

2.2.2 Garantía de fiabilidad de los datos

¿Qué garantías ofrece Kafka?

  • Kafka puede garantizar el orden de los mensajes de partición. Si se utiliza el mismo productor para escribir mensajes en la misma partición y el mensaje B se escribe después del mensaje A, entonces Kafka puede garantizar que el desplazamiento del mensaje B sea mayor que el del mensaje A y los consumidores lo leerán primero. Vaya al mensaje A y lea el mensaje B.

  • Solo cuando un mensaje se escribe en todas las copias de la partición, se considera "confirmado". El productor puede optar por recibir diferentes tipos de confirmaciones, como la confirmación cuando el mensaje se envía por completo, la confirmación cuando el mensaje se escribe al líder de la partición o la confirmación cuando el mensaje se envía a la red.

  • Mientras haya una copia activa, la información enviada no se perderá.

  • Los consumidores solo pueden leer los mensajes enviados.

Copiar

El mecanismo de replicación de Kafka y la arquitectura de copias múltiples particionadas son el núcleo de la garantía de confiabilidad de Kafka. Escribir el mensaje en varias copias permite a Kafka seguir garantizando la durabilidad del mensaje en caso de un bloqueo.

El tema de Kafka se divide en varias particiones, que son bloques de datos básicos. Cada partición puede tener varias copias, una de las cuales es la líder. Todos los eventos se envían a la copia del jefe o se leen los eventos directamente desde la copia del jefe. Otras copias solo necesitan mantenerse sincronizadas con la copia líder y copiar los últimos eventos en el tiempo.

El líder mantiene un conjunto de réplicas sincronizadas (ISR) dinámico, lo que significa un conjunto de seguidores que está sincronizado con el líder. Cuando el seguidor en el ISR completa la sincronización de datos, el líder enviará un ack. Si el seguidor no sincroniza los datos con el líder durante mucho tiempo, el seguidor será expulsado del ISR. El umbral de tiempo lo establece el parámetro replica.lag.time.max.ms . Cuando el líder no está disponible, se elegirá un nuevo líder del ISR. Se deben cumplir las siguientes condiciones para ser considerado síncrono:

  • Hay una sesión activa con zookeeper, es decir, ha enviado un latido al zookeeper en los últimos 6 segundos (configurable).

  • Obtenga los datos más recientes del líder en los últimos 10 (configurable).

Configuraciones que afectan la confiabilidad del almacenamiento de mensajes de Kafka

Análisis del principio de Kafka y la estrategia de asignación de particiones

mecanismo de respuesta ack

Para algunos datos menos importantes, los requisitos de confiabilidad de los datos no son muy altos y se puede tolerar una pequeña pérdida de datos, por lo que no es necesario esperar a que todos los seguidores en el ISR reciban correctamente. Por lo tanto, Kafka ofrece tres niveles de confiabilidad y los usuarios pueden pesarlos según los requisitos de confiabilidad y latencia. acks:

  •  0: El  productor no espera el recibo del corredor. Esta operación proporciona el retraso más bajo. El corredor regresa tan pronto como se recibe el recibo y no se ha escrito en el disco. Cuando el corredor falla, se pueden perder datos;

  •  1: El  productor espera el reconocimiento del líder y regresa después de que el líder de la partición se haya colocado correctamente. Si el líder falla antes de que la sincronización del seguidor sea exitosa, los datos se perderán;

  •  -1 (todo): el productor espera el reconocimiento del corredor, el líder de la partición y el seguidor en el ISR antes de regresar al reconocimiento. Pero si el líder falla después de que se completa la sincronización del seguidor y antes de que el corredor envíe el acuse de recibo, se generarán datos duplicados. (En casos extremos, se pueden perder datos: cuando solo hay un líder en el ISR, equivale a 1).

Garantía de consistencia de consumo

Análisis del principio de Kafka y la estrategia de asignación de particiones

(1) Fallo del seguidor

 Después de que el seguidor falla, será expulsado temporalmente del ISR. Después de que el seguidor se recupere, el seguidor leerá el último HW registrado en el disco local, interceptará la parte del archivo de registro más alta que el HW y comenzará la sincronización del HW al líder.

Después de que el LEO del seguidor sea mayor o igual que el HW de la partición, es decir, después de que el seguidor alcance al líder, puede volver a unirse al ISR.

(2) Fallo del líder

 Después de que el líder falle, se seleccionará un nuevo líder del ISR. Después de eso, para garantizar la coherencia de los datos entre varias copias, los seguidores restantes primero cortarán las partes de sus archivos de registro que sean más altas que el HW y luego reiniciarán el nuevo. El líder sincroniza datos.

 Nota: Esto solo puede garantizar la coherencia de los datos entre copias, pero no garantiza que los datos no se pierdan ni se dupliquen.

2.2.3 Proceso de envío de mensajes

El productor de Kafka envía mensajes de forma asincrónica. En el proceso de envío de mensajes, están involucrados dos hilos: el hilo principal y el hilo del remitente, y un hilo compartido variable RecordAccumulator. El hilo principal envía mensajes al RecordAccumulator, y el hilo del remitente extrae continuamente mensajes del RecordAccumulator y los envía al corredor de Kafka.

Análisis del principio de Kafka y la estrategia de asignación de particiones

Para mejorar la eficiencia, los mensajes se escriben a Kafka en lotes. Un lote es un grupo de mensajes que pertenecen al mismo tema y partición. (Si cada mensaje viaja a través de la red por separado, causará una gran sobrecarga de la red. Dividir el mensaje en lotes puede reducir la sobrecarga de la red. Sin embargo, existe una compensación entre el retraso de tiempo y el rendimiento: cuanto mayor es el lote, la unidad de tiempo Cuantos más mensajes se procesen en el interior, mayor será el tiempo de transmisión de un solo mensaje). Los datos por lotes se comprimirán, lo que puede mejorar la transmisión de datos y la capacidad de almacenamiento, pero requiere más procesamiento de cálculos.

Parámetros relacionados:

  • batch.size: el remitente enviará datos solo después de que los datos se hayan acumulado en batch.size. (Unidad: byte, nota: no el número de mensajes).

  • linger.ms : si los datos no alcanzan el tamaño de lote, el remitente enviará los datos después de esperar a que se  produzca linger.ms . (Unidad: milisegundos).

  • client.id : este parámetro puede ser cualquier cadena, el servidor lo usará para identificar la fuente del mensaje y también se puede usar en registros e indicadores de cuotas.

  • max.in .flight.requests.per.connection: este parámetro especifica cuántos mensajes puede enviar el productor antes de recibir la respuesta del servidor. Cuanto mayor sea su valor, más memoria ocupará, pero también aumentará el rendimiento. Establecerlo en 1 puede garantizar que los mensajes se escriban en el servidor en el orden en que se envían, incluso si se produce un reintento.

2.3 Consumidores de Kafka

2.3.1 Método de consumo

 El consumidor usa el modo de extracción para leer datos del corredor.

 El modelo push es difícil de adaptar a consumidores con diferentes tasas de consumo, porque la tasa de envío de mensajes la determina el corredor. Su objetivo es entregar mensajes lo más rápido posible, pero esto puede hacer que los consumidores lleguen demasiado tarde para procesar los mensajes. Las manifestaciones típicas son la denegación de servicio y la congestión de la red. El modo pull puede consumir mensajes a un ritmo adecuado según la capacidad de consumo del consumidor.

 La desventaja del modo de extracción es que si Kafka no tiene datos, los consumidores pueden caer en un bucle y siempre devolver datos vacíos. En respuesta a esto, el consumidor de Kafka pasará un tiempo de espera de parámetro de duración al consumir datos. Si actualmente no hay datos para consumir, el consumidor esperará un período de tiempo antes de regresar.

2.3.2 Estrategia de asignación de particiones

 Hay varios consumidores en un grupo de consumidores y varias particiones en un tema, por lo que la asignación de particiones seguramente estará involucrada, es decir, determinar qué partición consume cada consumidor. Kafka proporciona tres estrategias de asignación de particiones de consumidores: RangeAssigor, RoundRobinAssignor, StickyAssignor.

 La interfaz PartitionAssignor se utiliza para la implementación definida por el usuario de algoritmos de asignación de particiones para lograr la asignación de particiones entre consumidores. Los miembros del grupo de consumidores se suscriben a los temas que les interesan y pasan esta relación de suscripción al Broker como coordinador del grupo de suscripción. El coordinador selecciona uno de los consumidores para realizar la asignación de partición del grupo de consumidores y envía el resultado de la asignación a todos los consumidores del grupo de consumidores. Kafka usa el algoritmo de asignación de RangeAssignor de forma predeterminada.

2.3.2.1 RangeAssignor

 RangeAssignor realiza una asignación de partición independiente para cada tema. Para cada tema, primero ordene las particiones de acuerdo con el ID de partición y luego ordene los consumidores del grupo de consumidores que se suscribe a este tema, y ​​luego distribuya las particiones a los consumidores de la manera más uniforme posible. Esto solo se puede equilibrar tanto como sea posible, porque es posible que la cantidad de particiones no se divida de manera uniforme por la cantidad de consumidores, por lo que algunos consumidores se asignarán a algunas particiones. El diagrama de distribución es el siguiente:

Análisis del principio de Kafka y la estrategia de asignación de particiones

El algoritmo de asignación de particiones es el siguiente:


@Override
public Map<String, List<TopicPartition>> assign(Map<String, Integer> partitionsPerTopic,
                                                Map<String, Subscription> subscriptions) {
    Map<String, List<String>> consumersPerTopic = consumersPerTopic(subscriptions);
    Map<String, List<TopicPartition>> assignment = new HashMap<>();
    for (String memberId : subscriptions.keySet())
        assignment.put(memberId, new ArrayList<TopicPartition>());
    //for循环对订阅的多个topic分别进行处理
    for (Map.Entry<String, List<String>> topicEntry : consumersPerTopic.entrySet()) {
        String topic = topicEntry.getKey();
        List<String> consumersForTopic = topicEntry.getValue();

        Integer numPartitionsForTopic = partitionsPerTopic.get(topic);
        if (numPartitionsForTopic == null)
            continue;
        //对消费者进行排序
        Collections.sort(consumersForTopic);
        //计算平均每个消费者分配的分区数
        int numPartitionsPerConsumer = numPartitionsForTopic / consumersForTopic.size();
        //计算平均分配后多出的分区数
        int consumersWithExtraPartition = numPartitionsForTopic % consumersForTopic.size();

        List<TopicPartition> partitions = AbstractPartitionAssignor.partitions(topic, numPartitionsForTopic);
        for (int i = 0, n = consumersForTopic.size(); i < n; i++) {
            //计算第i个消费者,分配分区的起始位置
            int start = numPartitionsPerConsumer * i + Math.min(i, consumersWithExtraPartition);
            //计算第i个消费者,分配到的分区数量
            int length = numPartitionsPerConsumer + (i + 1 > consumersWithExtraPartition ? 0 : 1);
            assignment.get(consumersForTopic.get(i)).addAll(partitions.subList(start, start + length));
        }
    }
    return assignment;
}

Un problema obvio con este método de distribución es que a medida que aumenta el número de temas suscritos por los consumidores, el problema de desequilibrio se volverá cada vez más grave. Por ejemplo, en el escenario de 4 particiones y 3 consumidores en la figura anterior, C0 asignará uno más. Dividir. Si se suscribe a un tema con un número de partición de 4 en este momento, entonces C0 asignará una partición más que C1 y C2, de modo que C0 asignará dos particiones más que C1 y C2, y a medida que el tema aumente, esta situación Empeorará. Resultado de distribución:

Análisis del principio de Kafka y la estrategia de asignación de particiones

Suscríbete a 2 temas, cada tema tiene 4 particiones, un total de 3 consumidores

  • C0 : [T0P0 , T0P1 , T1P0 , T1P1]

  • C1: [T0P2 , T1P2]

  • C2: [T0P3 , T1P3]

2.3.2.2 RoundRobinAssignor

La estrategia de asignación de RoundRobinAssignor es ordenar todas las particiones de tema y todos los consumidores suscritos en el grupo de consumidores y asignarlos de la manera más uniforme posible (RangeAssignor se ordena y asigna para una sola partición de tema). Si en el grupo de consumidores, la lista de temas suscritos por los consumidores es la misma (cada consumidor se suscribe al mismo tema), entonces el resultado de la distribución es lo más equilibrado posible (la diferencia en el número de particiones asignadas entre los consumidores no excederá 1). Si las listas de temas suscritos son diferentes, entonces no se garantiza que el resultado de la distribución sea "lo más equilibrado posible", porque algunos consumidores no participan en la distribución de algunos temas.

Análisis del principio de Kafka y la estrategia de asignación de particiones

En comparación con la estrategia de asignación anterior de RangeAssignor, los dos temas anteriores pueden hacer que la asignación de particiones sea más equilibrada. Pero considerando esta situación, suponga que hay tres consumidores C0, C1, C2, tres Topic T0, T1, T2, respectivamente, con 1, 2 y 3 particiones, y C0 se suscribe a T0, C1 se suscribe a T0 y T1, C2 se suscribe a T0, T1 y T2, entonces el resultado de la asignación de RoundRobinAssignor es el siguiente:

Análisis del principio de Kafka y la estrategia de asignación de particiones

Parece que la distribución ha sido lo más equilibrada posible, pero se puede encontrar que C2 se encarga del consumo de 4 particiones y C1 se suscribe a T1 ¿Es más equilibrado dar T1P1 a C1 para consumo?

2.3.2.3 StickyAssignor

El algoritmo de asignación de partición StickyAssignor tiene como objetivo ajustar los cambios de asignación de partición lo menos posible en función de los resultados de la asignación anterior cuando se realiza una nueva asignación, y ahorrar la sobrecarga causada por los cambios de asignación de partición. Sticky es "pegajoso", lo que puede entenderse como el resultado de la distribución es "pegajoso": cada cambio de distribución hace el menor cambio en relación con la distribución anterior. Hay dos objetivos:

  • La distribución de particiones es lo más equilibrada posible.

  • El resultado de cada redistribución debe ser lo más coherente posible con el resultado de la asignación anterior.

Cuando estos dos objetivos entran en conflicto, se da prioridad al primero. El primer objetivo es que cada algoritmo de asignación intente lograr tanto como sea posible, mientras que el segundo objetivo refleja realmente las características de StickyAssignor.

El algoritmo StickyAssignor es más complicado. El siguiente ejemplo ilustra el efecto de la asignación (en comparación con RoundRobinAssignor). La condición previa:

  • Hay 4 temas: T0, T1, T2 y T3, y cada tema tiene 2 particiones.

  • Hay 3 consumidores: C0, C1 y C2. Todos los consumidores se suscriben a estas 4 particiones.

Análisis del principio de Kafka y la estrategia de asignación de particiones

La flecha roja de arriba representa la asignación de partición modificada Se puede ver que la estrategia de asignación de StickyAssignor tiene pocos cambios.

2.3.3 Mantenimiento de compensación

Dado que el Consumidor puede tener fallas de energía y otras fallas durante el proceso de consumo, después de que el Consumidor se recupera, necesita continuar consumiendo desde la ubicación antes de la falla. Por lo tanto, el Consumidor necesita registrar dónde consume en tiempo real para que pueda continuar consumiendo luego de que se restablezca la falla. Antes de la versión 0.9 de Kafka, Consumer guarda el desplazamiento en zookeeper de forma predeterminada. A partir de la versión 0.9, Consumer guarda el desplazamiento en un tema incorporado de Kafka llamado _consumeroffsets de forma predeterminada. No se puede leer de forma predeterminada. Se puede leer configurando exclude.internal.topics = false en consumr.properties.

2.3.4 Kafka lee y escribe datos de manera eficiente (comprende)

Escritura secuencial en disco

Los datos de producción del productor de Kafka deben escribirse en el archivo de registro. El proceso de escritura debe agregarse al final del archivo, que es escritura secuencial. Los datos muestran que para el mismo disco, la escritura secuencial puede alcanzar los 600M / s, mientras que la escritura aleatoria es de solo 100K / s. Esto está relacionado con la estructura mecánica del disco La razón por la que la escritura secuencial es rápida es porque ahorra mucho tiempo de direccionamiento de cabeza.

Tecnología de copia cero

La tarea principal de la copia cero es evitar que la CPU copie datos de un almacenamiento a otro. El objetivo principal es utilizar varias tecnologías de copia cero para evitar que la CPU realice una gran cantidad de tareas de copia de datos, reducir las copias innecesarias o dejar que otros componentes lo hagan. Hacer este tipo de tarea simple de transferencia de datos libera a la CPU para que se concentre en otras tareas. Esto puede hacer que el uso de los recursos del sistema sea más eficaz.

referencias

  1. Documento chino de Kafka

  2. [Serie Kafka] especifica un desplazamiento, ¿cómo puedo encontrar el mensaje correspondiente?

  3. Tutorial de Kafka (Inicio rápido de Kafka Framework)

  4. Análisis de la estrategia de asignación de particiones de Kafka: StickyAssignor

  5. Almacenamiento de registros de Kafka

  6. Análisis de la tecnología Zero Copy en Linux

  7. La guía definitiva de Kafka

Autor: Li Xiaobing, del equipo de tecnología de Internet vivo

Supongo que te gusta

Origin blog.51cto.com/14291117/2595398
Recomendado
Clasificación