Este documento aborda as diferenças entre as arquiteturas baseadas em filas de mensagens no local e as arquiteturas baseadas em eventos na nuvem que são implementadas no Pub/Sub. A tentativa de aplicar padrões no local diretamente a tecnologias baseadas na nuvem pode perder o valor único que torna a nuvem apelativa em primeiro lugar.
Este documento destina-se a arquitetos de sistemas que estão a migrar designs de arquiteturas no local para designs baseados na nuvem. Este documento pressupõe que tem conhecimentos básicos dos sistemas de mensagens.
O diagrama seguinte mostra uma vista geral de um modelo de fila de mensagens e um modelo de publicação/subscrição.
No diagrama anterior, um modelo de fila de mensagens é comparado com um modelo de fluxo de eventos de publicação/subscrição. Num modelo de fila de mensagens, o publicador envia mensagens para uma fila onde cada subscritor pode ouvir uma fila específica. No modelo de fluxo de eventos que usa o Pub/Sub, o publicador envia mensagens para um tópico que vários subscritores podem ouvir. As diferenças entre estes modelos são descritas nas secções seguintes.
Comparação entre streams de eventos e mensagens baseadas em filas
Se trabalha com sistemas no local, já conhece os autocarros de serviços empresariais (ESBs) e as filas de mensagens. Os streams de eventos são um novo padrão e existem diferenças importantes com vantagens concretas para os sistemas modernos em tempo real.
Este documento aborda as principais diferenças no mecanismo de transporte e nos dados de conteúdo útil na arquitetura orientada por eventos.
Transporte de mensagens
Os sistemas que movem dados nestes modelos são denominados agentes de mensagens e existem várias frameworks implementadas nos mesmos. Um dos primeiros conceitos é o mecanismo subjacente que transporta mensagens do publicador para o destinatário. Nas estruturas de mensagens no local, o sistema de origem emite uma mensagem explícita, remota e desassociada para um sistema de processamento a jusante através de uma fila de mensagens como transporte.
O diagrama seguinte mostra um modelo de fila de mensagens:
No diagrama anterior, as mensagens fluem de um processo de publicador a montante para um processo de subscritor a jusante através de uma fila de mensagens.
O sistema A (o publicador) envia uma mensagem para uma fila no agente de mensagens designado para o sistema B (o subscritor). Embora o subscritor da fila possa consistir em vários clientes, todos esses clientes são instâncias duplicadas do sistema B implementadas para escalabilidade e disponibilidade. Se forem necessários processos a jusante adicionais, por exemplo, o sistema C, para consumir as mesmas mensagens do produtor (sistema A), é necessária uma nova fila. Tem de atualizar o produtor para publicar as mensagens na nova fila. Este modelo é frequentemente denominado transmissão de mensagens.
A camada de transporte de mensagens para estas filas pode ou não oferecer garantias de ordem das mensagens. Frequentemente, espera-se que as filas de mensagens forneçam um modelo com garantia de ordem com dados sequenciados num modelo de acesso FIFO (first-in-first-out) rigoroso, semelhante a uma fila de tarefas. Este padrão é inicialmente fácil de implementar, mas, eventualmente, apresenta desafios de escalabilidade e operacionais. Para implementar mensagens ordenadas, o sistema precisa de um processo central para organizar os dados. Este processo limita as capacidades de escalabilidade e reduz a disponibilidade do serviço, uma vez que é um único ponto de falha.
Os agentes de mensagens nestas arquiteturas tendem a implementar lógica adicional, como monitorizar que subscritor recebeu que mensagens e monitorizar a carga do subscritor. Os subscritores tendem a ser meramente reativos, não tendo conhecimento do sistema geral e executando simplesmente uma função após a receção de uma mensagem. Estes tipos de arquiteturas são denominados smart pipes (sistema de fila de mensagens) e dumb endpoints (subscritor).
Transporte do Pub/Sub
Semelhante aos sistemas orientados para mensagens, os sistemas de streaming de eventos também transportam mensagens de um sistema de origem para sistemas de destino separados. No entanto, em vez de enviar cada mensagem para uma fila segmentada por processos, os sistemas baseados em eventos tendem a publicar mensagens num tópico partilhado e, em seguida, um ou mais recetores subscrevem esse tópico para ouvir mensagens relevantes.
O diagrama seguinte mostra como várias mensagens são emitidas por um publicador a montante para um único tópico e, em seguida, são encaminhadas para o subscritor a jusante relevante:
É deste padrão de publicação e subscrição que deriva o termo pub/sub. Este padrão também é a base do Google Cloud produto denominado Pub/Sub. Ao longo deste documento, pubsub refere-se ao padrão e Pub/Sub refere-se ao produto.
No modelo pubsub, o sistema de mensagens não precisa de saber nada sobre os subscritores. Não monitoriza as mensagens que foram recebidas e não gere a carga no processo de consumo. Em vez disso, os subscritores monitorizam as mensagens que foram recebidas e são responsáveis pela autogestão dos níveis de carga e do escalamento.
Uma vantagem significativa é que, à medida que encontra novas utilizações para os dados no modelo pub/sub, não precisa de atualizar o sistema de origem para publicar em novas filas nem duplicar dados. Em alternativa, associa o novo consumidor a uma nova subscrição sem qualquer impacto no sistema existente.
As chamadas nos sistemas de streaming de eventos são quase sempre assíncronas, enviam eventos e não esperam por nenhuma resposta. Os eventos assíncronos permitem maiores opções de escalabilidade para o produtor e os consumidores. No entanto, este padrão assíncrono pode criar desafios se estiver à espera de garantias de ordem das mensagens FIFO.
Dados da fila de mensagens
Os dados transmitidos entre sistemas em sistemas de filas de mensagens e sistemas baseados em publicação/subscrição são geralmente denominados mensagem em ambos os contextos. No entanto, o modelo em que esses dados são apresentados é diferente. Nos sistemas de filas de mensagens, as mensagens refletem um comando destinado a alterar o estado dos dados a jusante. Se analisar os dados dos sistemas de filas de mensagens no local, o publicador pode indicar explicitamente o que o consumidor deve fazer. Por exemplo, uma mensagem de inventário pode indicar o seguinte:
<m:SetInventoryLevel>
<inventoryValue>3001</inventoryValue>
</m: SetInventoryLevel>
Neste exemplo, o produtor está a indicar ao consumidor que tem de definir o nível de inventário como 3001. Esta abordagem pode ser desafiante porque o produtor tem de compreender a lógica empresarial de cada consumidor e tem de criar estruturas de mensagens separadas para diferentes exemplos de utilização. Este sistema de fila de mensagens era uma prática comum com os grandes monolíticos que a maioria das empresas implementava. No entanto, se quiser avançar mais rapidamente, aumentar a escala e inovar mais do que antes, estes sistemas centralizados podem tornar-se um obstáculo, porque a mudança é arriscada e lenta.
Este padrão também apresenta desafios operacionais. Quando ocorrem dados incorretos, registos duplicados ou outros problemas que precisam de ser corrigidos, este modelo de mensagens apresenta um desafio significativo. Por exemplo, se precisar de reverter a mensagem usada no exemplo anterior, não sabe qual o valor a definir para o valor corrigido porque não tem nenhuma referência ao estado anterior. Não tem informações sobre se o valor do inventário era 3000 ou 4000 antes de essa mensagem ser enviada.
Dados do Pubsub
Os eventos são outra forma de enviar dados de mensagens. O que é único é que os sistemas orientados por eventos focam-se no evento ocorrido em vez do resultado que deve ocorrer. Em vez de enviar dados que indicam a ação que um consumidor deve realizar, os dados focam-se nos detalhes do evento real produzido. Pode implementar sistemas orientados por eventos numa variedade de plataformas, mas são frequentemente vistos em sistemas baseados em publicação/subscrição.
Por exemplo, um evento de inventário pode ter o seguinte aspeto:
{ "inventory":-1 }
Os dados de eventos anteriores indicam que ocorreu um evento que diminuiu o inventário em 1. As mensagens focam-se no evento que ocorreu no passado e não num estado a ser alterado no futuro. Os publicadores podem enviar mensagens de forma assíncrona, o que facilita a expansão dos sistemas orientados por eventos em comparação com os modelos de fila de mensagens. No modelo pub/sub, pode desassociar a lógica empresarial para que o produtor só precise de compreender as ações realizadas e não precise de compreender os processos a jusante. Os subscritores desses dados podem escolher a melhor forma de lidar com os dados que recebem. Uma vez que estas mensagens não são comandos imperativos, a ordem das mensagens torna-se menos importante.
Com este padrão, é mais fácil reverter as alterações. Neste exemplo, não são necessárias informações adicionais porque pode negar o valor do inventário para o mover na direção oposta. As mensagens que chegam atrasadas ou desordenadas já não são um problema.
Comparação de modelos
Neste cenário, tem quatro artigos do mesmo produto no seu inventário. Um cliente devolve uma unidade do produto e o cliente seguinte compra três unidades desse mesmo produto. Para este cenário, suponha que a mensagem para o produto devolvido sofreu um atraso.
A tabela seguinte compara o nível de inventário do modelo de fila de mensagens que recebe a contagem de inventário na ordem correta com o mesmo modelo que recebe a contagem de inventário fora de ordem:
Fila de mensagens (ordem correta) | Fila de mensagens (desordenada) |
---|---|
Inventário inicial: 4 |
Inventário inicial: 4 |
Mensagem 1: setInventory(5) |
Mensagem 2: setInventory(2) |
Mensagem 2: setInventory(2) |
Mensagem 1: setInventory(5) |
Nível de inventário: 2 |
Nível de inventário: 5 |
No modelo de fila de mensagens, a ordem em que as mensagens são recebidas é importante porque a mensagem contém o valor pré-calculado. Neste exemplo, se as mensagens chegarem na ordem correta, o nível de inventário é 2. No entanto, se as mensagens chegarem fora de ordem, o nível de inventário é 5, o que é incorreto.
A tabela seguinte compara o nível de inventário do sistema baseado em pubsub que recebe a contagem de inventário pela ordem correta com o mesmo sistema que recebe a contagem de inventário fora de ordem:
Pubsub (ordem correta) | Pubsub (fora de ordem) |
---|---|
Inventário inicial: 4 | Inventário inicial: 4 |
Mensagem 2: "inventory":-3 |
Mensagem 1: "inventory":+1 |
Mensagem 1: "inventory":+1 |
Mensagem 2: "inventory":-3 |
Nível de inventário: 2 |
Nível de inventário: 2 |
No sistema baseado em pubsub, a ordem das mensagens não é importante porque é informada pelos serviços que produzem eventos. Independentemente da ordem em que as mensagens chegam, o nível de inventário é preciso.
O diagrama seguinte mostra como, no modelo de fila de mensagens, a fila executa comandos que indicam ao subscritor como o estado deve mudar, enquanto no modelo de publicação/subscrição, os subscritores reagem aos dados de eventos que indicam o que ocorreu no publicador:
Implementar arquiteturas orientadas por eventos
Existem vários conceitos a considerar ao implementar arquiteturas orientadas por eventos. As secções seguintes apresentam alguns desses tópicos.
Garantias de entrega
Um conceito que surge numa discussão sobre o sistema é a fiabilidade das garantias de entrega de mensagens. Diferentes fornecedores e sistemas podem oferecer diferentes níveis de fiabilidade, por isso, é importante compreender as variações.
O primeiro tipo de garantia faz uma pergunta simples: se uma mensagem for enviada, a entrega é garantida? Isto é o que se denomina uma entrega, pelo menos, uma vez. A mensagem é garantidamente entregue pelo menos uma vez, mas pode ser enviada mais do que uma vez.
Um tipo diferente de garantia é o fornecimento no máximo uma vez. Com a entrega, no máximo, uma vez, a mensagem só é entregue um máximo de uma vez, mas não existem garantias de que seja efetivamente entregue.
A variação final para as garantias de fornecimento é o fornecimento exatamente uma vez. Neste modelo, o sistema envia apenas uma cópia da mensagem que tem garantia de entrega.
Ordem e duplicados
Em arquiteturas no local, as mensagens seguem frequentemente um modelo FIFO. Para alcançar este modelo, um sistema de processamento centralizado gere a sequenciação das mensagens para garantir uma ordenação precisa. As mensagens ordenadas criam desafios porque, para qualquer mensagem com falha, todas as mensagens têm de ser reenviadas em sequência. Qualquer sistema centralizado pode tornar-se um desafio para a disponibilidade e a escalabilidade. Normalmente, a expansão de um sistema central que gere as encomendas só é possível adicionando mais recursos a uma máquina existente. Com um único sistema a gerir a encomenda, quaisquer problemas de fiabilidade afetam todo o sistema e não apenas essa máquina.
Os serviços de mensagens altamente escaláveis e disponíveis usam frequentemente vários sistemas de processamento para garantir que as mensagens são entregues, pelo menos, uma vez. Com muitos sistemas, não é possível garantir a gestão da ordem das mensagens.
As arquiteturas orientadas por eventos não dependem da ordem das mensagens e podem tolerar mensagens duplicadas. Se for necessária uma ordem, os subsistemas podem implementar técnicas de agregação e de janelas. No entanto, esta abordagem sacrifica a escalabilidade e a disponibilidade nesse componente.
Técnicas de filtragem e fanout
Uma vez que uma stream de eventos pode conter dados que podem ou não ser necessários para todos os subscritores, existe frequentemente a necessidade de limitar os dados que um determinado subscritor recebe. Existem dois padrões para gerir este requisito: filtros de eventos e fanouts de eventos.
O diagrama seguinte mostra um sistema orientado por eventos com filtros de eventos a filtrar mensagens para subscritores:
No diagrama anterior, os filtros de eventos usam mecanismos de filtragem que limitam os eventos que chegam ao subscritor. Neste modelo, um único tópico contém todas as variações de uma mensagem. Em vez de um subscritor ler cada mensagem e verificar se é aplicável, a lógica de filtragem no sistema de mensagens avalia a mensagem, impedindo que seja enviada aos outros subscritores.
O diagrama seguinte mostra uma variação do padrão de filtro de eventos denominado event fanout que usa vários tópicos:
No diagrama anterior, o tópico principal contém todas as variações de uma mensagem, mas um mecanismo de fanout de eventos republica as mensagens em tópicos relacionados com esse subconjunto de subscritores.
Filas de mensagens não processadas
Mesmo nos melhores sistemas, podem ocorrer falhas. As filas de mensagens não processadas são uma técnica para lidar com essas falhas. Na maioria das arquiteturas orientadas por eventos, o sistema de mensagens continua a fornecer uma mensagem a um subscritor até que este a confirme.
Se existir um problema com uma mensagem, por exemplo, carateres inválidos no corpo da mensagem, o subscritor pode não conseguir confirmar a receção da mensagem. O sistema pode não conseguir processar o cenário ou pode até terminar o processo.
Normalmente, os sistemas tentam novamente enviar mensagens não reconhecidas ou com erros. Se uma mensagem inválida não for reconhecida após um período predeterminado, o evento de mensagem acaba por expirar e é removido do tópico. Do ponto de vista operacional, é útil rever as mensagens em vez de estas desaparecerem. É aqui que entram as filas de mensagens não processadas. Em vez de remover a mensagem do tópico, a mensagem é movida para outro tópico onde pode ser reprocessada ou revista para compreender o motivo pelo qual ocorreu um erro.
Histórico de streams e repetições
As streams de eventos são fluxos contínuos de dados. O acesso a estes dados do histórico é útil. Pode querer saber como um sistema atingiu um determinado estado. Pode ter perguntas relacionadas com a segurança que requerem uma auditoria dos dados. A capacidade de captar um registo histórico dos eventos é fundamental nas operações a longo prazo de um sistema orientado por eventos.
Uma utilização comum dos dados de eventos do histórico é usá-los com um sistema de repetição. As repetições são usadas para fins de teste. Ao repetir os dados de eventos da produção noutros ambientes, como o de preparação e o de teste, pode validar novas funcionalidades em conjuntos de dados reais. Também pode repetir dados do histórico para recuperar de um estado com falhas. Se um sistema ficar inativo ou perder dados, as equipas podem repetir o histórico de eventos a partir de um ponto válido conhecido e o serviço pode reconstruir o estado que perdeu.
A captura destes eventos em filas baseadas em registos ou streams de registo também é útil quando os subscritores precisam de acesso a uma sequência de eventos em momentos diferentes. Os registos de streams podem ser vistos em sistemas com capacidades offline. Ao usar o histórico de streams, pode processar as novas entradas mais recentes lendo a stream a partir do ponteiro last-read.
Vistas de dados: tempo real e quase em tempo real
Com todos os dados a fluírem através dos sistemas, é importante que consiga usar os dados. Existem muitas técnicas para aceder e usar estas streams de eventos, mas um exemplo de utilização comum é compreender o estado geral dos dados num momento específico. Estas são frequentemente perguntas orientadas para cálculos, como "quantos" ou "nível atual", que podem ser usadas por outros sistemas ou para consumo humano. Existem várias implementações que podem responder a estas perguntas:
- Um sistema em tempo real pode ser executado continuamente e acompanhar o estado atual. No entanto, como o sistema só tem um cálculo na memória, qualquer tempo de inatividade define o cálculo como zero.
- O sistema pode calcular valores a partir da tabela de histórico para cada pedido, mas isto pode tornar-se um problema porque tentar calcular valores para cada pedido à medida que os dados aumentam pode tornar-se inviável.
- O sistema pode criar instantâneos dos cálculos em intervalos específicos, mas a utilização apenas dos instantâneos não reflete os dados em tempo real.
Um padrão útil a implementar é uma arquitetura Lambda com capacidades quase em tempo real e em tempo real. Por exemplo, uma página de produto num site de comércio eletrónico pode usar visualizações de dados de inventário quase em tempo real. Quando os clientes fazem encomendas, é usado um serviço em tempo real para garantir atualizações de estado do inventário atualizadas ao segundo. Para implementar este padrão, o serviço responde a pedidos quase em tempo real de uma tabela de instantâneos que contém valores calculados num determinado intervalo. Um pedido em tempo real usa a tabela de instantâneo e os valores na tabela de histórico desde o último instantâneo para obter o estado atual exato. Estas vistas materializadas dos streams de eventos fornecem dados acionáveis para impulsionar processos empresariais reais.
O que se segue?
- Leia sobre o Pub/Sub ou o Cloud Tasks para a transmissão de mensagens e a integração assíncrona.
- Experimente o início rápido do Pub/Sub.
- Consulte a vista geral da arquitetura do Pub/Sub
- Explore arquiteturas de referência, diagramas e práticas recomendadas sobre o Google Cloud. Consulte o nosso Centro de arquitetura na nuvem.