Seguimiento de un problema de respuesta lenta del clúster Kafka en la memoria del editor
Proporcionaron una copia de los resultados de sus propias pruebas, que mostraron que el retraso de respuesta promedio para enviar datos al grupo A de Kafka estaba dentro de los 10 ms, pero el retraso de respuesta promedio para enviar datos al grupo B de Kafka alcanzó más de 2000 ms.
Este tipo de problemas suele ser un dolor de cabeza. Primero, monitoreamos y alertamos al clúster de Kafka. Al verificar la disponibilidad, los cambios de tráfico, los registros de Kafka, etc., no encontramos nada inusual. En segundo lugar, los tiempos de respuesta lentos también pueden estar relacionados con la forma en que los usuarios utilizan y prueban los métodos.
Así que como primer paso decidí verificar que el problema existía.
En el directorio kafka/bin, kafka proporciona un script de prueba de rendimiento de solicitud de escritura, a saber, kafka-producer-perf-test.sh.
Este script ejecuta la clase kafka.perf.ProducerPerformance en kafka, envía mensajes a kafka y genera un informe CSV.
El comando de prueba es el siguiente:
kafka/bin/kafka-producer-perf-test.sh --broker-list ${BROKER_LIST} --topics perf-test- topic -- show-detailed-stats --messages 10000 --csv-reporter-enabled --metrics-dir .
Al analizar el informe generado, encontramos que efectivamente hay un nodo que está respondiendo lentamente. :
Puede ver que la distribución de P999 ha alcanzado aproximadamente 1300 ms, lo que obviamente no es normal, pero ¿cuál es el motivo?
Como no hay ningún problema con el registro, solo podemos ver la información de jstack:
Como se mencionó anteriormente, la información en jstack es muy extraña y muchos kafka-request-handler Los hilos están bloqueados.
La siguiente es una breve explicación del modelo de subproceso de procesamiento de solicitudes de Kafka, citando el diagrama en el artículo Comunicación de red Kafka NIO:
Como se muestra en la figura, Kafka usa el modelo selector de Java NIO. Un subproceso Aceptador es responsable de aceptar solicitudes y varios subprocesos de Procesador son responsables de procesar solicitudes. Pero en realidad, el hilo del Procesador simplemente encapsula la solicitud en una solicitud Kafka y la coloca en RequestChannel (por supuesto, también son responsables de leer la respuesta y devolverla, por lo que no lo ampliaré aquí). Lo que realmente ejecuta la solicitud es KafkaRequestHandler, el hilo kafka-request-handler en jstack.
Por lo tanto, cuando hay una gran cantidad de bloqueo en el subproceso kafka-request-handler, afectará en gran medida la eficiencia de respuesta de todo el nodo.
Con respecto al estado BLOQUEADO en los subprocesos de Java, puede ver directamente la descripción del documento de Java:
Se puede ver que la razón por la cual el subproceso kafka-request-handler está bloqueado es el bloqueo. agarrando.
Encontramos el código fuente basado en kafka.cluster.Partition.appendMessagesToLeader en el mensaje jstack:
Puede ver que este método es realmente sincrónico y el objeto de sincronización es leaderIsrUpdateLock. leaderIsrUpdateLock es una variable miembro de kafka.cluster.Partition, lo que significa que las esperas exclusivas solo ocurrirán cuando se escriba en la misma partición de tema.
Por lo tanto, la única razón por la que ocurre el problema anterior es que un tema tiene una gran cantidad de solicitudes de escritura, pero el tema no tiene una gran cantidad de particiones, lo que resulta en una concurrencia insuficiente.
Por lo tanto, una gran cantidad de ProduceRequests de este tema ocupan el grupo de subprocesos kafka-request-handler, pero estos subprocesos compiten por bloqueos entre sí, lo que resulta en una baja eficiencia de ejecución, lo que resulta en solicitudes de otros temas no siendo procesado a tiempo.
Al analizar los registros y observar el tráfico monitoreado, se puede determinar que el QPS de las solicitudes de ProduceRequest de un tema en el clúster representa más del 80 del QPS de todo el clúster.
Al observar el número de entradas de mensajes por unidad de tiempo (MessagesInPerSec) y el número de solicitudes enviadas por unidad de tiempo (ProduceRequestPerSec) en los indicadores de seguimiento del tema, se puede calcular que hay menos de Se activarán un promedio de 10 mensajes del tema. Considerando el número de partición, se puede inferir que la empresa debe adoptar el modo de sincronización del productor de Kafka, es decir, se activa una solicitud de escritura de Kafka para cada mensaje. .
Hay dos soluciones:
Por supuesto, aumentar el número de particiones de temas también puede aliviar el problema hasta cierto punto, porque las solicitudes de escritura entre diferentes particiones no son mutuamente excluyentes, pero Este enfoque se parece más a tratar los síntomas que a la causa raíz, encubriendo el problema subyacente.
En un sistema distribuido, es muy importante enviar las solicitudes de red de manera adecuada, generalmente en lotes de manera "menos es más" para mejorar la eficiencia y equilibrar la puntualidad y el rendimiento. Demasiadas solicitudes pequeñas no solo reducen el rendimiento sino que también abruman los servicios de backend.
Por supuesto, como proveedor de servicios, debe tomar medidas como multiinquilino y restricciones de tráfico para evitar sobrecargar el servicio en escenarios de uso inusuales.