Entendendo o Spark Streaming com Kafka e Druid

Como cientista da computação e engenheiro de dados, estou lidando com tecnologias de Big Data, como Spark Streaming, Kafka e Apache Druid. Todos eles têm seus próprios tutoriais e páginas RTFM .

No entanto, ao combinar essas tecnologias em alta escala, você pode encontrar a solução que cobre casos de uso de produção mais complicados.
Neste artigo, compartilharei o conhecimento adquirido ao combinar o Spark Streaming, o Kafka e o Apache Druid, todos juntos para criar um painel de análise em tempo real, garantindo uma representação precisa dos dados.

Antes de nos aprofundarmos ... algumas palavras sobre o Real Time Analytics

A análise em tempo real é uma nova tendência nas tecnologias de Big Data e geralmente tem um efeito comercial significativo. Ao analisar dados atualizados, os insights são mais precisos. Por exemplo, fornecer um painel de análise em tempo real para as equipes de Analistas de Dados, BI e Gerentes de Contas pode ajudar essas equipes a tomar decisões rápidas.
A arquitetura comumente usada para análises em tempo real em escala é baseada no Spark Streaming e no Kafka. Ambas as tecnologias são muito bem escaláveis. Eles rodam em clusters e dividem a carga entre muitas máquinas. A saída dos trabalhos do Spark pode ir para vários destinos diferentes, depende do caso de uso específico e da arquitetura. Nosso objetivo era fornecer a ferramenta visual exibindo eventos em tempo real. Para esse fim, escolhemos o banco de dados Apache Druid.

Visualização de dados no Apache Druid

O Druid é um banco de dados de análise em tempo real de alto desempenho. Um de seus benefícios é a capacidade de consumir dados em tempo real do tópico Kafka e criar visualizações poderosas sobre ele usando o módulo Pivot . Suas visualizações permitem a execução de várias consultas ad-hoc "fatia e corte" e obtêm resultados visuais rapidamente. É muito útil para analisar vários casos de uso, por exemplo, o desempenho de campanhas específicas em determinados países. Os dados são recuperados em tempo real, com um atraso de 1-2 minutos.

A arquitetura

Por isso, decidimos construir nosso sistema de análise em tempo real com base em eventos Kafka e Apache Druid. Já tivemos eventos no tópico Kafka. No entanto, não podemos apenas ingeri-los no Druid como estão. Precisávamos adicionar mais dimensões a cada evento. Precisávamos enriquecer cada evento com mais dados para vê-lo no Druid de uma maneira conveniente. Em relação à escala, estamos lidando com centenas de milhares de eventos por minuto, portanto, precisamos usar a tecnologia que possa suportar esses números. Decidimos usar o trabalho Spark Streaming para enriquecer os eventos Kafka originais.

Figura 1. Arquitetura de análise em tempo real

O trabalho de Streaming Spark é executado para sempre? Na verdade não.

A idéia do trabalho do Spark Streaming é que ele esteja sempre em execução. O trabalho nunca deve parar. Ele lê constantemente eventos do tópico Kafka, os processa e grava a saída em outro tópico Kafka. No entanto, esta é uma visão otimista. Na vida real, as coisas são mais complicadas. Há falhas no driver no cluster Spark, nesse caso a tarefa é reiniciada. Às vezes, a nova versão do aplicativo spark é implementada em produção. O que acontece nesse caso? Como o trabalho reiniciado lê o tópico Kafka e processa os eventos? Antes de analisarmos esses detalhes, esta figura mostra o que vemos no Druid quando o trabalho de Streaming Spark é reiniciado:

Figura 3. Pico de dados de reinicialização da tarefa

Podemos ver que o uso do recurso de confirmação automática do Kafka causa um novo efeito. Não há "perda de dados", mas agora vemos eventos duplicados. Não houve uma verdadeira "explosão" de eventos. O que realmente aconteceu é que o mecanismo de confirmação automática comete compensações "de tempos em tempos". Existem muitas mensagens no tópico de saída que não foram confirmadas. Após a reinicialização, o trabalho consome mensagens das últimas compensações confirmadas e processa alguns desses eventos novamente . É por isso que na saída temos uma explosão de eventos.

Claramente, incorporar essas duplicações em nossa visualização pode enganar os consumidores comerciais desses dados e afetar suas decisões e confiar no sistema.

Etapa 2: Confirmar compensações Kafka manualmente

Portanto, não podemos confiar no recurso de confirmação automática do Kafka. Precisamos cometer compensações de Kafka por nós mesmos. Para isso, vamos ver como o Spark Streaming consome dados de tópicos do Kafka. O Spark Streaming usa uma arquitetura chamada Discretized Streams, ou DStream. O DStream é representado por uma série contínua de RDDs ( conjuntos de dados distribuídos resilientes ), que é uma das principais abstrações do Spark. A maioria dos trabalhos do Spark Streaming é semelhante a:

dstream.foreachRDD {rdd =    rdd.foreach {record = process (record)}}

No nosso caso, processar o registro significa gravá-lo no tópico Kafka de saída. Portanto, para cometer compensações de Kafka, precisamos fazer o seguinte:

dstream.foreachRDD {rdd =   val offsetRanges = rdd.asInstanceOf [HasOffsetRanges] .offsetRanges   rdd.foreach {record = process (record)}   stream.asInstanceOf [CanCommitOffsets] .commitAsync (offsetRanges) }

 

Essa é uma abordagem direta e, antes de discutirmos mais profundamente, vamos dar uma olhada no quadro geral. Vamos supor que lidamos com compensações corretamente. Ou seja, essas compensações são salvas após cada processamento do RDD. O que acontece quando paramos o trabalho? O trabalho é interrompido no meio do processamento do RDD. A parte do micr lote é gravada no tópico Kafka de saída e não é confirmada. Quando o trabalho for executado novamente, ele processará algumas mensagens pela segunda vez e o pico de mensagens duplicadas aparecerá (como antes) no Druid:

Figura 4. Pico de dados na reinicialização da tarefa

Desligamento Gracioso

Acontece que existe uma maneira de garantir que um trabalho não seja morto durante um processamento de RDD. É chamado de "desligamento gracioso". Existem várias postagens de blog descrevendo como você pode matar seu aplicativo Spark normalmente, mas a maioria delas se relaciona a versões antigas do Spark e tem muitas limitações. Estávamos procurando uma solução "segura" que funcionasse em qualquer escala e não dependesse de uma versão ou sistema operacional específico do Spark. Para ativar o Graceful Shutdown, o contexto do Spark deve ser criado com os seguintes parâmetros:
spark.streaming.stopGracefulOnShutdown = true.
Isso instrui o Spark a desligar o StreamingContext normalmente no desligamento da JVM, em vez de imediatamente.
Além disso, precisamos de um mecanismo para interromper nossos trabalhos intencionalmente, por exemplo, ao implantar uma nova versão. Implementamos a primeira versão desse mecanismo, simplesmente verificando a existência de um arquivo HDFS que instrui o encerramento da tarefa. Quando o arquivo aparecer no HDFS, o contexto de streaming será interrompido com os seguintes parâmetros:
ssc.stop (stopSparkContext = true, stopGracfully = true)

Nesse caso, o aplicativo Spark para normalmente apenas depois que todo o processamento de dados recebido é concluído. É exatamente disso que precisamos.

Etapa 3: Kafka commitAsync

Vamos recapitular o que temos até agora. Intencionalmente comprometemos as compensações Kafka em cada processamento de RDD (usando a API Kafka commitAsync) e usamos o desligamento normal. Aparentemente, houve outra ressalva. Analisando a documentação da API Kafka e do código-fonte Kafka commitAsync () , aprendi que o commitAsync () apenas coloca o offsetRanges em uma fila, que na verdade é processada apenas no próximo loop do foreachRDD. Mesmo que o trabalho do Spark seja parado normalmente e conclua o processamento de todos os seus RDDs, as compensações do último RDD não serão confirmadas. Para resolver esse problema, implementamos um código que persiste as compensações do Kafka de forma síncrona e não depende do Kafka commitAsync (). Para cada RDD, armazenamos as compensações confirmadas em um arquivo HDFS. Quando o trabalho começa a ser executado novamente, ele carrega o arquivo de compensações do HDFS e consome o tópico Kafka a partir dessas compensações e assim por diante.

Aqui funciona!

Foi apenas a combinação de um desligamento normal e um armazenamento síncrono de compensações Kafka que nos proporcionaram o resultado desejado. Sem perda de dados, sem picos durante a reinicialização:

Figura 5. Sem perda de dados de picos durante a reinicialização da tarefa do Spark

Conclusão

Resolver o problema de integração entre o Spark Streaming e o Kafka foi um marco importante para a construção do nosso painel de análise em tempo real. Encontramos a solução que garante um fluxo de dados estável sem perda de eventos ou duplicatas durante a reinicialização do trabalho do Spark Streaming. Agora temos os dados confiáveis ​​que são visualizados no Druid. Graças a isso, adicionamos mais tipos de eventos (tópicos sobre Kafka) ao Druid e criamos painéis em tempo real . Esses painéis fornecem informações para várias equipes, como BI, Produto e Suporte ao Cliente. Nosso próximo objetivo é utilizar mais recursos do Druid, como novas funções analíticas e alertas.

 

Máxima comunicação com proteção ao extremo? Avance Network: A verdadeira rede social junte-se a nós


Strong

5178 Blog indlæg

Kommentarer