Em um aplicativo empresarial tradicional, as solicitações dos clientes são executadas em uma transação do banco de dados. Geralmente, todos os dados necessários para concluir uma solicitação são armazenados em um único banco de dados que tenha propriedades ACID. O termo ACID significa atomicidade, consistência e isolamento. e durabilidade. Portanto, é possível garantir a consistência usando os recursos transacionais do sistema de banco de dados relacional. Quando algo der errado com a transação, o sistema de banco de dados poderá reverter automaticamente e repetir a transação. Neste documento, descrevemos como projetar fluxos de trabalho transacionais usando o Cloud Run, Pub/Sub, Workflows e Firestore no modo Datastore (Datastore). Ele é destinado a desenvolvedores de aplicativos que querem projetar fluxos de trabalho transacionais em um aplicativo baseado em microsserviços.
Este documento pertence a uma série que tem as seguintes partes:
- Fluxos de trabalho transacionais na arquitetura de microsserviços no Google Cloud (este documento).
- Como implantar um aplicativo de exemplo de fluxos de trabalho transacionais na arquitetura de microsserviços: um tutorial que mostra como implantar e usar um aplicativo de exemplo com a arquitetura descrita neste documento.
Transações de ponta a ponta em microsserviços
Em arquiteturas de microsserviços, uma transação de ponta a ponta pode abranger vários serviços. Cada serviço pode fornecer um recurso específico e ter o próprio banco de dados independente, conforme mostrado no diagrama a seguir.
Conforme mostrado na imagem anterior, um cliente acessa vários microsserviços por meio de um gateway. Por causa dessa disposição de acesso do cliente, não é possível confiar nos recursos transacionais de um único banco de dados para ajudar seu sistema de banco de dados a se recuperar de falhas e garantir consistência. Em vez disso, recomendamos a implementação de um fluxo de trabalho transacional na sua arquitetura de microsserviços.
Neste documento, descrevemos dois padrões que podem ser usados para implementar um fluxo de trabalho transacional em uma arquitetura de microsserviços. Os padrões são os seguintes:
- Saga baseada em coreografias
- Orquestração síncrona
Exemplo de aplicativo
Para ajudar a demonstrar o fluxo de trabalho, este documento usa um aplicativo de exemplo
simples, que pode processar transações de pedido para um site de compras. O
aplicativo gerencia clientes e pedidos. Os clientes têm um limite de crédito, e o aplicativo precisa confirmar que um novo pedido não excederá o limite de crédito do cliente. Conforme mostrado no diagrama a seguir, o fluxo de trabalho transacional é executado
nos seguintes microsserviços: os serviços Order
e Customer
.
O fluxo de trabalho é o seguinte:
- Um cliente envia uma solicitação de pedido que especifica um ID de cliente e vários itens.
- O serviço
Order
atribui um ID de pedido e armazena as informações do pedido no banco de dados. O status do pedido está marcado comopending
. - O serviço
Customer
aumenta o uso de crédito do cliente armazenado no banco de dados de acordo com o número de itens pedidos. Por exemplo, um aumento de 100 créditos em um único item. - Se o uso total de crédito for menor ou igual ao limite predefinido, o pedido será aceito e o serviço
Order
mudará o status do pedido no banco de dados paraaccepted
. - Se o uso total de crédito for maior que o limite predefinido, o serviço
Order
mudará o status do pedido pararejected
. Nesse caso, o uso de crédito não aumenta.
O serviço Order
O serviço Order
gerencia o status de um pedido. Ele gera e armazena um
registro de pedido no banco de dados Order
para cada solicitação do cliente. O registro consiste nas seguintes colunas:
Order_id
: o ID do pedido. Esse ID é gerado pelo serviçoOrder
.Customer_id
: o ID de cliente.Number
: a quantidade de itens no pedido.Status
: o status de um pedido.
Atendimento ao cliente
O serviço Customer
gerencia os créditos dos clientes. Ele gera e armazena um
registro do cliente no banco de dados Customer
para cada solicitação do cliente. O registro consiste nas seguintes colunas:
Customer_id
: o ID do cliente, que é gerado pelo serviçoCustomer
.Credit
: o número de créditos do cliente. Ele aumenta quando o cliente faz o pedido de itens.Limit
: o limite de crédito individual do cliente. Um pedido será rejeitado quando o crédito do cliente estiver acima do limite definido.
Saga baseada em coreografias
Nesta seção, descrevemos como implementar um padrão de microsserviços de saga baseada em coreografia em um fluxo de trabalho transacional.
Visão geral da arquitetura
Em um padrão de microsserviços de saga baseada em coreografia, eles funcionam como um sistema distribuído automaticamente. Quando um serviço muda o status da própria entidade, ele publica um evento para notificar outros serviços sobre atualizações. O evento de notificação aciona outros serviços para agir. Dessa forma, vários serviços funcionam juntos para concluir um processo transacional. A comunicação entre microsserviços é assíncrona. Quando um serviço publica um evento, nenhuma informação é enviada ao serviço de publicação para confirmar os serviços que recebem o evento ou quando o recebem.
A imagem a seguir mostra um exemplo de padrão de microsserviços de saga com base em coreografia.
O exemplo de arquitetura mostrado na imagem anterior é o seguinte:
- O Cloud Run atua como um ambiente de execução de microsserviços.
- O Pub/Sub atua como um serviço de mensagens para entregar eventos entre microsserviços.
- O Datastore fornece um banco de dados de cada serviço.
Use o Datastore para armazenar eventos antes de publicá-los. Conforme explicado no processo de publicação de eventos, os microsserviços armazenam os eventos em vez de publicá-los imediatamente.
Os serviços Order
e Customer
armazenam eventos no banco de dados do evento primeiro.
Em seguida, os eventos armazenados são publicados periodicamente usando o Cloud Scheduler.
O Cloud Scheduler
invoca o serviço event-publisher
, que publica eventos. Esse fluxo de eventos
é mostrado na seguinte imagem:
Fluxo de trabalho transacional
Em um fluxo de trabalho transacional, dois serviços se comunicam entre si por meio de eventos. Nesta arquitetura, o pedido do cliente é processado da seguinte maneira:
- O cliente envia uma solicitação de pedido que especifica o ID do cliente
e o número de itens que ele solicitou. A solicitação é enviada ao
serviço
Order
por uma API REST. - O serviço
Order
atribui um ID ao pedido e armazena as informações do pedido no banco de dadosOrder
. O status do pedido é marcado comopending
. O serviçoOrder
retorna as informações do pedido para o cliente e publica um evento que inclui essas informações no seguinte tópico do Pub/Sub:order-service-event
. - O serviço
Customer
recebe o evento por uma notificação push. Isso aumenta o uso de crédito do cliente, que é armazenado no banco de dadosCustomer
de acordo com o número de itens pedidos. - Se o uso total de créditos for menor ou igual ao limite predefinido, o serviço
Customer
publicará um evento que declara que o aumento do crédito foi bem-sucedido. Como alternativa, ele publica um evento que afirma que o aumento do crédito falhou. Nesse caso, o uso de crédito não é aumentado. - O serviço
Order
recebe o evento por uma notificação push. Ele altera o status do pedido paraaccepted
ourejected
. O cliente pode rastrear o status do pedido usando o ID do pedido retornado do serviçoOrder
.
O diagrama a seguir resume esse fluxo de trabalho:
O processo de publicação de eventos
Quando um microsserviço modifica os próprios dados no banco de dados e publica um evento para notificá-lo, essas duas operações precisam ser realizadas de maneira atômica. Por exemplo, se o microsserviço falhar após a modificação de dados sem publicar um evento, o processo transacional será interrompido. Nesse caso, os dados podem ser deixados em um estado inconsistente nos vários microsserviços envolvidos na transação. Para evitar esse problema, no aplicativo de exemplo usado neste documento, os microsserviços gravam dados de eventos no banco de dados de back-end em vez de publicar diretamente os eventos no Pub/Sub.
Os dados são modificados e os dados de eventos associados são gravados de maneira atômica, usando o recurso transacional do banco de dados de back-end. Esse padrão, mostrado na imagem a seguir, é chamado de eventos de aplicativo ou "caixa de saída transacional".
Conforme mostrado na imagem anterior, inicialmente a coluna published
nos dados do evento está marcada como False
. Em seguida, o serviço event-publisher
verifica periodicamente o banco de dados e publica eventos em que a coluna published
é False
.
Depois de publicar um evento, o serviço event-publisher
muda a coluna published
para True
.
Conforme mostrado na imagem a seguir, os bancos de dados Order
e Event
no mesmo namespace podem ser atualizados atomicamente por
transações do Datastore.
Se o serviço event-publisher
falhar após a publicação de um evento sem alterar a coluna published
, ele publicará o mesmo evento novamente após a recuperação. Como a republicação do evento causa um evento duplicado, os microsserviços que recebem o evento precisam verificar a possível duplicação e processá-la de acordo. Essa abordagem ajuda a garantir a idempotência do gerenciamento de eventos.
A imagem a seguir mostra como um aplicativo de exemplo lida com a duplicação de eventos.
Conforme mostrado no diagrama anterior, o aplicativo lida com eventos duplicados com o seguinte fluxo de trabalho:
- Cada microsserviço atualiza o respectivo banco de dados de back-end com base na lógica de negócios acionada por um evento e grava o ID no respectivo banco de dados.
- Essas duas gravações são realizadas de uma forma atômica, com o recurso transacional usado pelos bancos de dados de back-end.
- Se os serviços receberem um evento duplicado, ele será detectado quando procurarem o ID do evento nos bancos de dados.
O processamento de eventos duplicados é uma prática comum no recebimento de eventos do Pub/Sub, porque há uma pequena chance de que o Pub/Sub possa causar uma entrega de mensagens duplicada.
Expandir a arquitetura
No aplicativo de exemplo, antes de processar uma mensagem, use
o Datastore para verificar se ela está duplicada. Essa abordagem significa que
o serviço que consome as mensagens (serviço Customer
, neste caso) é
idempotente. Essa abordagem geralmente é chamada de
padrão "consumidor idempotente". Alguns frameworks implementam esse padrão como um recurso integrado, por
exemplo,
Eventuate.
No entanto, acessar o banco de dados sempre que você processar uma mensagem pode causar problemas de desempenho. Uma solução é utilizar um banco de dados com bom desempenho e escalonabilidade, por exemplo, Redis.
Orquestração síncrona
Nesta seção, descrevemos como implementar um padrão de microsserviços de orquestração síncrono em um fluxo de trabalho transacional.
Visão geral da arquitetura
Nesse padrão, um único orquestrador controla o fluxo de execução de uma transação. A comunicação entre os microsserviços e o orquestrador é feita de maneira síncrona por meio das APIs REST.
Na arquitetura de exemplo descrita neste documento, o Cloud Run é usado como um ambiente de execução de microsserviços e o Datastore é usado como um banco de dados de back-end para cada serviço. Além disso, os fluxos de trabalho são usados como um orquestrador. Esse padrão é mostrado na seguinte imagem:
Fluxo de trabalho transacional
Na arquitetura de um fluxo de trabalho síncrono, o pedido de um cliente é processado da seguinte maneira:
- O cliente envia uma solicitação de pedido que especifica o ID de um
cliente e o número de itens que ele solicitou. A solicitação é enviada ao
serviço
Order processor
pela API REST. - O serviço
Order processor
executa um fluxo de trabalho em que o ID de cliente e o número de itens são transmitidos para os fluxos de trabalho. - O fluxo de trabalho chama a API REST do serviço
Order
e transmite o ID do cliente e o número de itens que o cliente solicitou. Em seguida, o serviçoOrder
atribui um ID de pedido ao pedido do cliente e armazena as informações do pedido no banco de dadosOrder
. O status do pedido está marcado comopending
. O serviçoOrder
retorna as informações do pedido para o fluxo de trabalho. - O fluxo de trabalho chama a API REST do serviço
Customer
e transmite o ID do cliente e o número de itens que o cliente solicitou. Em seguida, o serviçoCustomer
aumenta o uso de crédito do cliente armazenado no banco de dadosCustomer
de acordo com o número de itens pedidos. - Se o uso total de créditos for menor ou igual ao limite predefinido, o serviço
Customer
retornará dados que explicam que o aumento de crédito foi bem-sucedido. Como alternativa, ele retorna dados que explicam que o aumento do crédito falhou. Nesse caso, o uso de crédito não é aumentado. - O fluxo de trabalho chama a API REST
Order
do serviço para mudar o status do pedido paraaccepted
ourejected
, conforme apropriado. Por fim, ele retorna as informações do pedido na atualização do status final para o serviçoOrder processor
. Em seguida, o serviçoOrder processor
retorna essas informações ao cliente.
Esse fluxo de trabalho é resumido no diagrama a seguir:
Vantagens e desvantagens
Ao considerar se é necessário implementar uma saga baseada em coreografia ou orquestração síncrona, a melhor opção para a organização é sempre o padrão mais adequado para as necessidades dela. No entanto, em geral, devido à simplicidade do design, a orquestração síncrona é geralmente a primeira escolha para muitas empresas.
Veja na tabela a seguir as vantagens e desvantagens da saga baseada em coreografia e os padrões de orquestração síncronas descritos neste documento.
Vantagens |
Desvantagens |
|
---|---|---|
Saga baseada em coreografia |
Acoplamento flexível: cada serviço publica eventos no Datastore quando há uma mudança nos próprios dados. Nenhuma informação é enviada para outros serviços. Essa abordagem torna cada serviço mais independente, e há menos chance de precisar modificá-los quando você introduz novos serviços no fluxo de trabalho. |
Dependência complexa: a implementação de todo o fluxo de trabalho é distribuída entre os serviços. Como resultado, pode ser complexo entender o fluxo de trabalho. Essa abordagem pode acidentalmente introduzir complexidade em futuras mudanças de design e solução de problemas. |
Orquestração síncrona |
Dependência simples: um único orquestrador controla todo o fluxo de execução de uma transação. Como resultado, é mais simples entender como funciona o fluxo de transação. Esse padrão simplifica a modificação do fluxo de trabalho e a solução de problemas. |
Risco do acoplamento rígido: o Orchestrator central depende de todos os serviços que compõem o fluxo de trabalho transacional. Como resultado, quando você modifica um desses serviços ou adiciona novos serviços ao fluxo de trabalho, talvez seja necessário modificar o orquestrador. O esforço extra necessário pode superar o benefício de modificar e adicionar serviços de maneira mais independente à arquitetura de microsserviços em comparação com sistemas monolíticos. |
A seguir
- Saiba mais sobre arquiteturas de microsserviços.
- Confira arquiteturas de referência, diagramas e práticas recomendadas do Google Cloud. Confira o Centro de arquitetura do Cloud.