Uma arquitetura orientada a eventos consiste em produtores de eventos que geram um fluxo de eventos, consumidores de eventos que escutam esses eventos e canais de eventos que transferem eventos de produtores para consumidores.
Os eventos são entregues quase em tempo real, para que os consumidores possam responder imediatamente conforme os eventos ocorrem. Os produtores são separados dos consumidores. Um produtor não sabe quais consumidores estão ouvindo. Os consumidores também são separados uns dos outros; e cada consumidor vê todos os eventos. Isso difere do padrão de Consumidores Concorrentes, onde os consumidores removem mensagens de uma fila, e uma mensagem é processada apenas uma vez (presumindo que não haja erros). Em alguns sistemas, como IoT, os eventos devem ser incluídos em volumes muito altos.
Uma arquitetura orientada a eventos pode usar um modelo de publish/subscribe (também chamado de pub/sub) ou um modelo de fluxo de eventos.
Pub/sub: a infraestrutura de mensagens acompanha o controle de assinaturas. Quando um evento é publicado, ele envia o evento para cada assinante. Depois que um evento é recebido, ele não pode ser reproduzido e não será exibido para assinantes novos.
Streaming de eventos: eventos são gravados em um registro. Os eventos são estritamente ordenados (dentro de uma partição) e duráveis. Os clientes não assinam o fluxo, em vez disso, um cliente pode ler a partir de qualquer parte do fluxo. O cliente é responsável por avançar a sua posição no fluxo. Isso significa que um cliente pode participar a qualquer momento e pode reproduzir eventos.
No lado do consumidor, há algumas variações comuns:
Processamento de eventos simples. Um evento dispara imediatamente uma ação no consumidor. Por exemplo, você pode usar as Azure Functions com um gatilho de Barramento de Serviço para que uma função seja executada sempre que uma mensagem é publicada em um tópico do Barramento de Serviço.
Correlação básica de eventos. Um consumidor precisa processar um pequeno número de eventos de negócios 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 de eventos usando uma tecnologia, como o Azure Stream Analytics ou o Apache Storm. Por exemplo, você pode agregar as leituras de um dispositivo incorporado em uma janela de tempo e gerar uma notificação se a média móvel ultrapassar um certo limite.
Processamento de fluxo de eventos. Use uma plataforma de fluxo de dados, como o Hub IoT do Azure ou o Apache Kafka, como um pipeline para ingestão de eventos e os encaminhe para os processadores de fluxo. Os processadores de fluxo agem para processar ou transformar o fluxo. Pode haver vários processadores de fluxo para subsistemas diferentes do aplicativo. Esta abordagem é uma boa opção para cargas de trabalho de IoT.
A fonte dos eventos pode ser externa ao sistema, como dispositivos físicos em uma solução de IoT. Nesse caso, o sistema deve ser capaz de receber os dados no volume e taxa de transferência necessários para a fonte de dados.
Há duas abordagens principais para estruturar cargas de eventos. Quando você tiver controle sobre os consumidores do evento, tome essa decisão de estrutura de carga por consumidor; misturar abordagens conforme necessário em uma única carga de trabalho.
Incluindo todos os atributos necessários na carga: 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, principalmente após atualizações. O gerenciamento de contratos e o controle de versão também podem se tornar complexos.
Incluindo apenas chaves na carga: 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 pior do que a primeira abordagem, pois 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 torne um ponto único de falha no sistema. Também pode ser necessário ter várias instâncias para lidar com o volume e a frequência de eventos. Além disso, um único consumidor pode processar eventos em vários threads. Isso poderá criar desafios se os eventos tiverem que ser processados em ordem ou se solicitarem semânticas exatamente uma vez. Confira Minimizar Coordenação.
Há duas topologias principais em muitas arquiteturas orientadas a eventos:
Topologia do agente. 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 ou orquestração central, portanto, essa topologia pode ser muito dinâmica. Essa topologia é altamente desacoplada, 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 executadas 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 agente. Existe um mediador de eventos que gerencia 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 agente, 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, melhor tratamento de erros distribuídos e consistência de dados potencialmente melhor. Essa topologia introduz maior acoplamento entre os componentes, e o mediador de eventos pode se tornar um gargalo ou uma preocupação de confiabilidade.
Quando usar essa arquitetura
- Vários subsistemas devem processar os mesmos eventos.
- Processamento em tempo real com retardo mínimo de tempo.
- O processamento de eventos complexos, como a correspondência de padrões ou agregação em janelas de tempo.
- Alto volume e alta velocidade de dados, como IoT.
Benefícios
- Produtores e consumidores são separados.
- Nenhuma integração de ponta a ponta. É fácil adicionar novos consumidores ao sistema.
- Os consumidores podem responder aos eventos imediatamente assim que chegarem.
- Altamente escalável, elástico e distribuído.
- Os subsistemas têm exibições independentes do fluxo de evento.
Desafios
Entrega garantida.
Em alguns sistemas, especialmente em situações de IoT, é crucial garantir que os eventos sejam entregues.
Eventos processados em ordem ou exatamente uma vez.
Normalmente, cada tipo de consumidor é executado em várias instâncias de resiliência e escalabilidade. Isso pode criar um desafio se os eventos precisarem ser processados em ordem (dentro de um tipo de consumidor) ou se a lógica de processamento de mensagens idempotentes não estiver implementada.
Coordenar mensagens entre serviços.
Os processos empresariais geralmente envolvem vários serviços que publicam e assinam mensagens para obter um resultado consistente em toda uma carga de trabalho. Padrões de fluxo de trabalho, como o padrão Coreografia e a Orquestração de Saga, podem ser usados para gerenciar de maneira confiável os fluxos de mensagens em vários serviços.
Tratamento de erro.
A arquitetura orientada por eventos usa principalmente a comunicação assíncrona. O desafio da comunicação assíncrona é o tratamento de erros. Uma maneira de resolver esse problema é usar um processador de tratamento de erros separado. Assim, quando o consumidor de eventos apresenta um erro, ele envia imediatamente, e de forma assíncrona, o evento incorreto para o processador de tratamento de erros e segue em frente. O processador do manipulador de erros tenta corrigir o erro e envia o evento de volta ao canal de ingestão original. Mas se o processador de tratamento de erros falhar, ele poderá enviar o evento incorreto a um administrador para inspeção adicional. Se você usar um processador de tratamento de erros, os eventos incorretos serão processados fora de sequência quando forem reenviados.
Perda de dados.
Outro desafio da comunicação assíncrona é a perda de dados. Se algum dos componentes falhar antes de processar e entregar com êxito o evento ao 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 remova os eventos da fila somente quando o próximo componente confirmar o recebimento do evento. Esses recursos geralmente são conhecidos como modo de confirmação do cliente e suporte ao último participante.
Implementando um padrão tradicional de solicitação-resposta.
Às vezes, o produtor do evento exige uma resposta imediata do consumidor do evento, como obter a qualificação de um cliente antes de prosseguir com um pedido. Na arquitetura orientada a eventos, a comunicação síncrona pode ser obtida 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. Os consumidores de eventos processam a solicitação e enviam a resposta de volta por meio de uma fila de resposta. Essa abordagem geralmente utiliza uma ID de sessão para rastreamento, para que o produtor de eventos 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 apropriado de eventos.
A geração de 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ários 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. Essa abordagem permite que cada evento seja processado apenas por consumidores relevantes, evitando processamento desnecessário.
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, reduz o tempo e o custo do transporte, mas requer que o código de processamento procure as informações adicionais necessárias. Para obter mais informações sobre isso, dê uma olhada nesta postagem no blog.
- Embora uma solicitação seja visível apenas 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 sejam ou não destinados a consumi-los. Operando com uma mentalidade de "assumir violação", esteja atento a quais informações você inclui em eventos para evitar a exposição de informações não intencionais.
- 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 , pipes e filtros. A integração da arquitetura orientada a eventos melhora o desempenho do sistema, eliminando gargalos e fornecendo contrapressão durante altos 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.