Uma arquitetura orientada a eventos consiste em produtores de eventos que geram um fluxo de eventos, consumidores de eventos que ouvem esses eventos e canais de eventos que transferem eventos de produtores para consumidores.
Os eventos são entregues quase em tempo real, pelo que os consumidores podem responder imediatamente aos eventos à medida que estes ocorrem. Os produtores estão dissociados dos consumidores – um produtor não sabe quais os consumidores que estão a ouvir. Os consumidores também estão desassociados uns dos outros e cada consumidor vê todos os eventos. Isto difere de um padrão Consumidores Concorrentes, no qual os consumidores extraem as mensagens de uma fila e uma mensagem é processada apenas uma vez (partindo do princípio de que não existem erros). Em alguns sistemas, como IoT, os eventos têm de ser ingeridos em volumes muito elevados.
Uma arquitetura orientada a eventos pode usar um modelo de publicação/assinatura (também chamado de pub/sub) ou um modelo de fluxo de eventos.
Publicação/subscrição: a infraestrutura de mensagens mantém um registo das subscrições. Quando um evento é publicado, envia o evento para cada subscritor. Depois que um evento é recebido, ele não pode ser repetido e os novos assinantes não veem o evento.
Transmissão em fluxo de eventos: os eventos são escritos num registo. Os eventos estão criteriosamente ordenados (dentro de uma partição) e são duráveis. Os clientes não subscrevem o fluxo, em vez disso, um cliente pode ler a partir de qualquer ponto do fluxo. O cliente é responsável pela progressão da respetiva posição no fluxo. Isto significa que um cliente não só pode efetuar a adesão em qualquer altura como pode reproduzir eventos.
No lado do consumidor, existem algumas variações comuns:
Processamento de eventos simples. Um evento aciona imediatamente uma ação no consumidor. Por exemplo, pode utilizar as Funções do Azure com um acionador do Service Bus, de modo a que uma função seja executada sempre que é publicada uma mensagem num tópico do Service Bus.
Correlação básica de eventos. Um consumidor precisa processar um pequeno número de eventos comerciais discretos, geralmente correlacionados por algum identificador, persistindo algumas informações de eventos anteriores para usar ao processar eventos posteriores. Esse padrão é suportado por bibliotecas como NServiceBus e MassTransit.
Processamento de eventos complexos. Um consumidor processa uma série de eventos, procurando padrões nos dados do evento, usando uma tecnologia como o Azure Stream Analytics. Por exemplo, pode agregar as leituras de um dispositivo incorporado ao longo de um certo período de tempo e gerar uma notificação caso a média móvel ultrapasse um determinado limiar.
Processamento de fluxos de eventos. Utilize uma plataforma de transmissão em fluxo de dados, como o Hub IoT do Azure ou o Apache Kafka, como um pipeline para ingerir eventos e fornecê-los aos processadores de fluxos. Os processadores de fluxos entram em ação para processar ou transformar o fluxo. Podem existir vários processadores de fluxos para diferentes subsistemas da aplicação. Esta abordagem é uma boa opção para as cargas de trabalho de IoT.
A origem dos eventos pode ser externa ao sistema como, por exemplo, dispositivos físicos numa solução de IoT. Nesse caso, o sistema tem de ser capaz de ingerir os dados com o volume e o débito de que a origem de dados precisa.
Há duas abordagens principais para estruturar cargas úteis de eventos. Quando você tiver controle sobre os consumidores do seu evento, tome essa decisão de estrutura de carga útil por consumidor; misturando abordagens, conforme necessário, dentro de uma única carga de trabalho.
Incluindo todos os atributos necessários na carga útil: essa abordagem é usada quando você deseja que os consumidores tenham todas as informações disponíveis sem a necessidade de consultar uma fonte de dados externa. No entanto, isso pode levar a problemas de consistência de dados devido a vários sistemas de registro, particularmente após atualizações. O gerenciamento de contratos e o controle de versão também podem se tornar complexos.
Incluindo apenas chave(s) na carga útil: nessa abordagem, os consumidores recuperam os atributos necessários, como uma chave primária, para buscar independentemente os dados restantes de uma fonte de dados. Embora esse método ofereça melhor consistência de dados devido a um único sistema de registro, ele pode ter um desempenho inferior ao da primeira abordagem, uma vez que os consumidores devem consultar a fonte de dados com frequência. Há menos preocupações em relação ao acoplamento, largura de banda, gerenciamento de contratos ou controle de versão, pois os eventos são menores e os contratos mais simples.
No diagrama lógico acima, cada tipo de consumidor é mostrado como uma única caixa. Na prática, é comum ter várias instâncias de um consumidor, para evitar que o consumidor se transforme num ponto de falha único no sistema. Também poderão ser necessárias várias instâncias para processar o volume e a frequência dos eventos. Além disso, um único consumidor pode processar eventos de vários threads. Isso pode criar desafios se os eventos precisarem ser processados em ordem ou exigirem semântica exata uma vez. Veja Minimizar a Coordenação.
Há duas topologias principais em muitas arquiteturas controladas por eventos:
Topologia do broker. Os componentes transmitem ocorrências como eventos para todo o sistema, e outros componentes agem sobre o evento ou simplesmente ignoram o evento. Essa topologia é útil quando o fluxo de processamento de eventos é relativamente simples. Não há coordenação central ou orquestração, então essa topologia pode ser muito dinâmica. Essa topologia é altamente dissociada, o que ajuda a fornecer escalabilidade, capacidade de resposta e tolerância a falhas de componentes. Nenhum componente possui ou está ciente do estado de qualquer transação comercial de várias etapas e as ações são tomadas de forma assíncrona. Posteriormente, as transações distribuídas são arriscadas porque não há meios nativos para serem reiniciadas ou reproduzidas. O tratamento de erros e as estratégias de intervenção manual precisam ser cuidadosamente considerados, pois essa topologia pode ser uma fonte de inconsistência de dados.
Topologia do mediador. Essa topologia aborda algumas das deficiências da topologia do broker. Existe um mediador de eventos que gere e controla o fluxo de eventos. O mediador de eventos mantém o estado e gerencia o tratamento de erros e os recursos de reinicialização. Ao contrário da topologia do broker, os componentes transmitem ocorrências como comandos e apenas para canais designados, geralmente filas de mensagens. Não se espera que esses comandos sejam ignorados por seus consumidores. Essa topologia oferece mais controle, tratamento de erros melhor distribuído e consistência de dados potencialmente melhor. Essa topologia introduz maior acoplamento entre componentes, e o mediador de eventos pode se tornar um gargalo ou uma preocupação de confiabilidade.
Quando utilizar esta arquitetura
- Vários subsistemas têm de processar os mesmos eventos.
- Processamento em tempo real com um hiato de tempo mínimo.
- Processamento de eventos complexos, como a agregação ou correspondência de padrões ao longo de períodos de tempo.
- Volume e velocidade de dados elevados, como IoT.
Benefícios
- Os produtores e os consumidores estão desassociados.
- Sem integrações ponto-a-ponto. É fácil adicionar novos consumidores ao sistema.
- Os consumidores podem responder de imediato aos eventos, à medida que estes são recebidos.
- Altamente escalável, elástico e distribuído.
- Os subsistemas têm vistas independentes do fluxo de eventos.
Desafios
Entrega garantida.
Em alguns sistemas, especialmente em cenários de IoT, é fundamental garantir que os eventos são entregues.
Processamento de eventos por uma ordem ou exatamente uma vez.
Normalmente, cada tipo de consumidor é executado em várias instâncias, por uma questão de resiliência e escalabilidade. Isso pode criar um desafio se os eventos tiverem de ser processados em ordem (dentro de um tipo de consumidor) ou se a lógica de processamento de mensagens idempotente não for implementada.
Coordenação de mensagens entre serviços.
Os processos de negócios geralmente envolvem vários serviços, publicação e assinatura de mensagens para alcançar um resultado consistente em toda uma carga de trabalho. Padrões de fluxo de trabalho, como o padrão Coreográfico e a Orquestração Saga, podem ser usados para gerenciar de forma confiável os fluxos de mensagens em vários serviços.
Tratamento de erros.
A arquitetura orientada a eventos usa principalmente comunicação assíncrona. Um desafio com a comunicação assíncrona é o tratamento de erros. Uma maneira de resolver esse problema é usar um processador manipulador de erros separado. Assim, quando o consumidor de eventos experimenta um erro, ele envia imediatamente e de forma assíncrona o evento errado para o processador do manipulador de erros e segue em frente. O processador manipulador de erros tenta corrigir o erro e envia o evento de volta para o canal de ingestão original. Mas se o processador do manipulador de erros falhar, ele pode enviar o evento errado para um administrador para inspeção adicional. Se você usar um processador manipulador de erros, os eventos errados serão processados fora de sequência quando forem reenviados.
Perda de dados.
Outro desafio com a comunicação assíncrona é a perda de dados. Se algum dos componentes falhar antes de processar e entregar com êxito o evento ao seu próximo componente, o evento será descartado e nunca chegará ao destino final. Para minimizar a chance de perda de dados, persista os eventos em trânsito e remova ou desenfileire os eventos somente quando o próximo componente tiver confirmado o recebimento do evento. Esses recursos são geralmente conhecidos como modo de reconhecimento do cliente e suporte ao último participante.
Implementação de um padrão tradicional de solicitação-resposta.
Às vezes, o produtor do evento exige uma resposta imediata do consumidor do evento, como obter uma elegibilidade de cliente antes de prosseguir com um pedido. Na arquitetura orientada a eventos, a comunicação síncrona pode ser alcançada por meio de mensagens de solicitação-resposta.
Esse padrão geralmente é implementado utilizando várias filas - uma fila de solicitação e uma fila de resposta. O produtor de eventos envia uma solicitação assíncrona para uma fila de solicitações, pausa outra operação nessa tarefa e aguarda uma resposta na fila de respostas; efetivamente transformando isso em um processo síncrono. Em seguida, os consumidores de eventos processam a solicitação e enviam a resposta de volta por meio de uma fila de respostas. Essa abordagem geralmente utiliza um ID de sessão para rastreamento, para que o produtor do evento saiba qual mensagem na fila de resposta está relacionada à solicitação específica. A solicitação original também pode especificar o nome da fila de resposta, potencialmente efêmera, em um cabeçalho de resposta ou outro atributo personalizado mutuamente acordado.
Manter o número adequado de eventos.
Gerar um número excessivo de eventos refinados pode saturar e sobrecarregar o sistema, dificultando a análise eficaz do fluxo geral de eventos. Esse problema é exacerbado quando as alterações precisam ser revertidas. Por outro lado, a consolidação excessiva de eventos também pode criar problemas, resultando em processamento e respostas desnecessárias dos consumidores de eventos.
Para alcançar o equilíbrio certo, considere as consequências dos eventos e se os consumidores precisam inspecionar as cargas úteis do evento para determinar suas respostas. Por exemplo, se você tiver um componente de verificação de conformidade, pode ser suficiente publicar apenas dois tipos de eventos: compatível e não compatível. Esta abordagem permite que cada evento seja processado apenas por consumidores relevantes, evitando processamentos desnecessários.
Considerações adicionais
- A quantidade de dados a serem incluídos em um evento pode ser uma consideração significativa que afeta o desempenho e o custo. Colocar todas as informações relevantes necessárias para o processamento no próprio evento pode simplificar o código de processamento e salvar pesquisas adicionais. Colocar a quantidade mínima de informações em um evento, como apenas alguns identificadores, reduzirá o tempo e o custo de transporte, mas exige que o código de processamento procure qualquer informação adicional necessária. Para obter mais informações sobre isso, dê uma olhada nesta postagem do blog.
- Embora uma solicitação só seja visível para o componente de tratamento de solicitações, os eventos geralmente são visíveis para vários componentes em uma carga de trabalho, mesmo que esses componentes não os consumam ou não se destinem a consumi-los. Operando com uma mentalidade de "assumir violação", esteja atento às informações que você inclui em eventos para evitar a exposição não intencional de informações.
- Muitos aplicativos usam a arquitetura orientada a eventos como sua arquitetura principal; No entanto, essa abordagem pode ser combinada com outros estilos arquitetônicos, resultando em arquiteturas híbridas. As combinações comuns incluem microsserviços e tubos e filtros. A integração da arquitetura orientada a eventos melhora o desempenho do sistema, eliminando gargalos e fornecendo pressão de retorno durante grandes volumes de solicitações.
- Domínios específicos geralmente abrangem vários produtores de eventos, consumidores ou canais de eventos. As alterações em um domínio específico podem afetar muitos componentes.
Recursos relacionados
- Vídeo de discussão da comunidade sobre as considerações de escolher entre coreografia e orquestração.