Compartilhar via


Design resiliente de Hubs de Eventos e Functions

Tratamento de erros, design para idempotência e gerenciamento de comportamento de repetição são algumas das medidas críticas que você pode tomar para garantir que as funções acionadas pelos Hubs de Eventos sejam resilientes e capazes de lidar com grandes volumes de dados. Este artigo aborda esses conceitos essenciais e faz recomendações para soluções de streaming de eventos sem servidor.

O Azure fornece três serviços de mensagens principais que podem ser usados com o Azure Functions para dar suporte a uma ampla variedade de cenários exclusivos orientados a eventos. Devido ao seu modelo de consumidor particionado e à capacidade de ingerir dados a uma taxa elevada, os Hubs de Eventos do Azure geralmente são usados para cenários de streaming de eventos e big data. Para ver uma comparação detalhada dos serviços de mensagens do Azure, consulte Escolher entre os serviços de mensagens do Azure – Grade de Eventos, Hubs de Eventos e Barramento de Serviço.

Benefícios e desafios do streaming

Compreender os benefícios e as desvantagens dos fluxos ajuda você a avaliar como um serviço como Hubs de Eventos funciona. Você também precisa desse contexto ao tomar decisões arquitetônicas impactantes, solucionar problemas e otimizar o desempenho. Considere os seguintes conceitos principais sobre soluções com Hubs de Eventos e Functions:

  • Fluxos não são filas: Hubs de Eventos, Kafka e outras ofertas semelhantes criadas no modelo de consumidor particionado não oferecem suporte intrinsecamente a alguns dos principais recursos em um agente de mensagens como Barramento de Serviço. Talvez o maior indicador dessas diferenças seja o fato de que as leituras são não destrutivas. Isso garante que os dados lidos pelo host do Functions permaneçam disponíveis posteriormente. Em vez disso, as mensagens são imutáveis e permanecem para outros consumidores lerem, incluindo a potencial leitura do mesmo consumidor novamente. Por esse motivo, as soluções que implementam padrões como consumidores concorrentes podem ser mais bem atendidas com um agente de mensagens, como o Barramento de Serviço.

  • Falta suporte inerente a mensagens mortas: um canal de mensagem morta não é um recurso nativo nos Hubs de Eventos ou no Kafka. Muitas vezes, o conceito de mensagens mortas é integrado a uma solução de streaming para contabilizar dados que não podem ser processados. Essa funcionalidade não é intencionalmente um elemento inato nos Hubs de Eventos e só é adicionada no lado do consumidor para fabricar um comportamento ou efeito semelhante. Se você precisar de suporte a mensagens mortas, deverá revisar potencialmente sua escolha de serviço de um serviço de mensagens de streaming.

  • Uma unidade de trabalho é uma partição: em um agente de mensagens tradicional, uma unidade de trabalho é uma única mensagem. Em uma solução de streaming, uma partição é frequentemente considerada a unidade de trabalho. Se cada evento em um hub de eventos for tratado como uma mensagem distinta que requer processamento de pedidos ou manipulação de transações financeiras, isso sugere uma oportunidade de explorar um serviço de mensagens mais adequado para desempenho ou processamento ideal.

  • Sem filtragem do lado do servidor: uma das razões pelas quais os Hubs de Eventos são capazes de aumentar a escala e a produtividade é devido à baixa sobrecarga no próprio serviço. Recursos como filtragem do lado do servidor, índices e coordenação entre corretores não fazem parte da arquitetura dos Hubs de Eventos. As funções são ocasionalmente usadas para filtrar eventos roteando-os para outros hubs de eventos com base no conteúdo no corpo ou cabeçalho. Essa abordagem é comum no streaming de eventos, mas vem com a ressalva de que a função inicial lê e avalia cada evento.

  • Todo leitor deve ler todos os dados: como a filtragem do lado do servidor não está disponível, um consumidor lê sequencialmente todos os dados em uma partição. Isso inclui dados que podem não ser relevantes ou até mesmo estar malformados. Várias opções e estratégias podem ser utilizadas para compensar estes desafios, que serão abordadas mais adiante nesta seção.

Essas decisões de design significativas permitem que os Hubs de Eventos façam o que fazem melhor: dar suporte a um fluxo significativo de eventos e fornecer um serviço robusto e resiliente para os consumidores lerem. Cada aplicativo do consumidor é encarregado da responsabilidade de manter seus próprios deslocamentos do lado do cliente ou cursor para esses eventos. A baixa sobrecarga torna os Hubs de Eventos uma opção acessível e poderosa para streaming de eventos.

Idempotência

Um dos princípios básicos dos Hubs de Eventos do Azure é o conceito de entrega pelo menos uma vez. Essa abordagem garante que os eventos sempre serão entregues. Isso também significa que os eventos podem ser recebidos mais de uma vez, mesmo repetidamente, pelos consumidores como uma função. Por isso, é importante que uma função acionada pelo hub de eventos ofereça suporte ao padrão consumidor idempotente .

Trabalhar sob a suposição de entrega pelo menos uma vez, especialmente no contexto de uma arquitetura orientada a eventos, é uma abordagem responsável para o processamento confiável de eventos. Sua função deve ser idempotente para que o resultado do processamento do mesmo evento várias vezes seja o mesmo que processá-lo uma vez.

Eventos duplicados

Há vários cenários diferentes que podem resultar em eventos duplicados sendo entregues a uma função:

  • Ponto de verificação: se o host do Azure Functions falhar ou o limite definido para a frequência do ponto de verificação em lote não for atingido, um ponto de verificação não é criado. Como resultado, o deslocamento para o consumidor não é avançado e, na próxima vez que a função for chamada, ela será retomada a partir do último ponto de verificação. É importante notar que o ponto de verificação ocorre no nível de partição para cada consumidor.

  • Eventos duplicados publicados: muitas técnicas podem reduzir as chances de o mesmo evento ser publicado em um fluxo, mas o consumidor ainda é responsável por lidar com duplicatas de forma idempotente.

  • Confirmações ausentes: em algumas situações, uma solicitação de saída para um serviço pode ser bem-sucedida, no entanto, uma confirmação (ACK) do serviço nunca é recebida. Essa percepção pode resultar na crença de que a chamada de saída falhou e iniciar uma série de novas tentativas ou outros resultados da função. No final, eventos duplicados podem ser publicados ou um ponto de verificação não é criado.

Técnicas de eliminação de duplicação

Projetar suas funções para entrada idêntica deve ser a abordagem padrão adotada quando emparelhada com a associação de gatilho do Hub de Eventos. Você deve considerar as seguintes técnicas:

  • Procurando duplicatas: antes de processar, siga as etapas necessárias para validar que o evento deve ser processado. Em alguns casos, isso exige uma investigação para confirmar que ainda é válido. Também pode ser possível que o tratamento do evento não seja mais necessário devido à atualização de dados ou à lógica que invalida o evento.

  • Projetar eventos para idempotência: ao fornecer informações adicionais dentro do conteúdo do evento, pode ser possível garantir que o processamento delas várias vezes não terá efeitos prejudiciais. Tomemos o exemplo de um evento que inclui um valor para saque de uma conta bancária. Se não for tratado de forma responsável, é possível que possa diminuir o saldo de uma conta várias vezes. No entanto, se o mesmo evento incluir o saldo atualizado na conta, ele poderá ser usado para realizar uma operação de upsert no saldo da conta bancária. Esta abordagem de transferência de estado realizada por eventos requer ocasionalmente coordenação entre produtores e consumidores e deve ser utilizada quando fizer sentido para os serviços participantes.

Tratamento de erros e novas tentativas

O tratamento de erros e as novas tentativas são algumas das qualidades mais importantes dos aplicativos distribuídos e orientados a eventos, e as funções não são exceção. Para soluções de streaming de eventos, a necessidade de suporte adequado ao tratamento de erros é fundamental, pois milhares de eventos podem rapidamente se transformar em um número igual de erros se não forem tratados corretamente.

Diretrizes sobre tratamento de erro

Sem o tratamento de erros, pode ser complicado implementar novas tentativas, detectar exceções de tempo de execução e investigar problemas. Toda função deve ter pelo menos algum nível ou tratamento de erros. Algumas diretrizes recomendadas são:

  • Use o Application Insights: habilite e use o Application Insights para registrar erros e monitorar a integridade de suas funções. Esteja atento às opções de amostragem configuráveis para cenários que processam um alto volume de eventos.

  • Adicionar tratamento estruturado de erros: aplique as construções de tratamento de erros apropriadas para cada linguagem de programação e capture, registre e detecte exceções previstas e não tratadas em seu código de função. Por exemplo, use um bloco try/catch em C#, Java e JavaScript e aproveite os blocos try e except em Python para lidar com exceções.

  • Registro em log: capturar uma exceção durante a execução oferece uma oportunidade de registrar informações críticas que podem ser usadas para detectar, reproduzir e corrigir problemas de forma confiável. Registre a exceção, não apenas a mensagem, mas o corpo, a exceção interna e outros artefatos úteis que serão úteis mais tarde.

  • Não pegue e ignore exceções: uma das piores coisas que você pode fazer é pegar uma exceção e não fazer nada com ela. Se você pegar uma exceção genérica, registre-a em algum lugar. Se você não registrar erros, será difícil investigar bugs e problemas relatados.

Novas tentativas

Implementar a lógica de nova tentativa em uma arquitetura de streaming de eventos pode ser complexo. Suporte a tokens de cancelamento, contagens de novas tentativas e estratégias de recuo exponencial são apenas algumas das considerações que o tornam desafiador. Felizmente, o Functions fornece políticas de novas tentativas que podem compensar muitas dessas tarefas que você mesmo codificaria.

Vários fatores importantes que devem ser considerados ao usar as políticas de novas tentativas com a associação do Hub de Eventos, incluem:

  • Evite novas tentativas indefinidas: quando a configuração de contagem máxima de novas tentativas estiver definida como -1, a função faça novas tentativas indefinidamente. Em geral, as novas tentativas indefinidas devem ser usadas com moderação com o Functions e quase nunca com a associação de gatilho do Hub de Eventos.

  • Escolha a estratégia de nova tentativa apropriada: uma estratégia de atraso fixo pode ser ideal para cenários que recebem pressão de retorno de outros serviços do Azure. Nesses casos, o atraso pode ajudar a evitar a limitação e outras limitações encontradas nesses serviços. A estratégia de retirada exponencial oferece mais flexibilidade para intervalos de atraso de nova tentativa e é comumente usada na integração com serviços de terceiros, pontos de extremidade REST e outros serviços do Azure.

  • Mantenha os intervalos e as contagens de novas tentativas baixos: quando possível, tente manter um intervalo de de novas tentativas menor que um minuto. Além disso, mantenha o número máximo de novas tentativas em um número razoavelmente baixo. Essas configurações são especialmente pertinentes quando executadas no plano de Consumo do Functions.

  • Padrão de disjuntor: um erro de falha transitório de tempos em tempos é esperado e um caso de uso natural para novas tentativas. No entanto, se um número significativo de falhas ou problemas estiverem ocorrendo durante o processamento da função, pode fazer sentido parar a função, resolver os problemas e reiniciar mais tarde.

Uma conclusão importante para as políticas de novas tentativas no Functions é que ele é um recurso de melhor esforço para reprocessar eventos. Ele não substitui a necessidade de tratamento de erros, registro em log e outros padrões importantes que fornecem resiliência ao seu código.

Estratégias para falhas e dados corrompidos

Há várias abordagens notáveis que você pode usar para compensar problemas que surgem devido a falhas ou dados ruins em um fluxo de eventos. Algumas estratégias fundamentais são:

  • Parar o envio e a leitura: Para corrigir o problema subjacente, pause a leitura e gravação de eventos. O benefício dessa abordagem é que os dados não serão perdidos e as operações podem ser retomadas após a implementação de uma correção. Essa abordagem pode exigir um componente de disjuntor na arquitetura e, possivelmente, uma notificação aos serviços afetados para obter uma pausa. Em alguns casos, a interrupção de uma função pode ser necessária até que os problemas sejam resolvidos.

  • Descartar mensagens: se as mensagens não forem importantes ou forem consideradas não essenciais, considere seguir em frente e não processá-las. Essa abordagem não funciona para cenários que exigem consistência forte, como registrar movimentos em uma partida de xadrez ou transações baseadas em finanças. O tratamento de erros dentro de uma função é recomendado para capturar e soltar mensagens que não podem ser processadas.

  • Nova tentativa: há muitas situações que podem justificar o reprocessamento de um evento. O cenário mais comum seria um erro transitório encontrado ao chamar outro serviço ou dependência. Erros de rede, limites e disponibilidade de serviço e forte consistência são talvez os casos de uso mais frequentes que justificam tentativas de reprocessamento.

  • Mensagem morta: a ideia aqui é publicar o evento em um hub de eventos diferente para que o fluxo existente não seja interrompido. A percepção é de que ela é removida do caminho frequente e pode ser tratada mais tarde ou por um processo diferente. Essa solução é usada com frequência para lidar com mensagens ou eventos suspeitos. Cada função configurada com um grupo de consumidores diferente, ainda encontrará os dados ruins ou corrompidos em seu fluxo e deve lidar com eles de forma responsável.

  • Nova tentativa e mensagem morta: a combinação de várias novas tentativas antes de finalmente publicar em um fluxo de mensagens mortas uma vez que um limite é atingido, é outro método conhecido.

  • Usar um registro de esquema: um registro de esquema pode ser usado como uma ferramenta proativa para ajudar a melhorar a consistência e a qualidade dos dados. O Registro de esquema do Azure pode dar suporte à transição de esquemas junto com o controle de versão e diferentes modos de compatibilidade à medida que os esquemas evoluem. Em sua essência, o esquema serve como um contrato entre produtores e consumidores, o que poderia reduzir a possibilidade de dados inválidos ou corrompidos serem publicados no fluxo.

No final, não há uma solução perfeita e as consequências e compensações de cada uma das estratégias precisam ser examinadas minuciosamente. Com base nos requisitos, usar várias dessas técnicas em conjunto pode ser a melhor abordagem.

Colaboradores

Esse artigo é mantido pela Microsoft. Ele foi originalmente escrito pelos colaboradores a seguir.

Autor principal:

Para ver perfis não públicos do LinkedIn, entre no LinkedIn.

Próximas etapas

Antes de continuar, considere revisar estes artigos relacionados: