Descentralize a lógica de fluxo de trabalho e distribua responsabilidades para outros componentes dentro de um sistema.
Contexto e problema
Um aplicativo baseado em nuvem geralmente é dividido em vários pequenos serviços que trabalham juntos para processar uma transação comercial de ponta a ponta. Mesmo uma única operação (dentro de uma transação) pode resultar em várias chamadas ponto a ponto entre todos os serviços. O ideal é que esses serviços sejam acoplados de forma bem livre. É difícil projetar um fluxo de trabalho distribuído, eficiente e escalável, pois geralmente isso envolve uma comunicação complexa entre serviços.
Um padrão comum para comunicação é usar um serviço centralizado ou um orquestrador. As solicitações recebidas fluem por meio do orquestrador à medida que ele delega as operações aos respectivos serviços. Cada serviço apenas completa sua responsabilidade e não se envolve com o fluxo de trabalho em geral.
O padrão do orquestrador normalmente é implementado como software personalizado e tem conhecimento de domínio sobre as responsabilidades desses serviços. Um benefício é que o orquestrador pode consolidar o status de uma transação com base nos resultados de operações individuais conduzidas pelos serviços downstream.
No entanto, há algumas desvantagens. Adicionar ou remover serviços pode quebrar a lógica existente, pois você precisa reconectar partes do caminho de comunicação. Essa dependência torna a implementação do orchestrator complexa e difícil de manter. O orquestrador pode ter um impacto negativo na confiabilidade da carga de trabalho. Sob carga, ele pode introduzir gargalos de desempenho e ser o único ponto de falha. Ele também pode causar falhas em cascata nos serviços downstream.
Solução
Delegue a lógica de processamento de transações entre os serviços. Deixe que cada serviço decida e participe do fluxo de trabalho de comunicação para uma operação de negócios.
O padrão é uma maneira de minimizar a dependência do software personalizado que centraliza o fluxo de trabalho de comunicação. Os componentes implementam uma lógica comum à medida que coreografam o fluxo de trabalho entre si sem ter comunicação direta entre si.
Uma maneira comum de implementar uma coreografia é usar um agente de mensagens que armazena solicitações em buffer até que os componentes downstream as reivindiquem e as processem. A imagem mostra o processamento de solicitações por meio de um modelo de editor-assinante.
As solicitações de um cliente são enfileiradas como mensagens em um agente de mensagens.
Os serviços ou o assinante sondam o agente para determinar se podem processar a mensagem com base na lógica de negócios implementada. O agente também pode enviar mensagens para os assinantes que estão interessados nessa mensagem.
Cada serviço inscrito faz a operação dele conforme indicado pela mensagem e responde ao agente com êxito ou falha da operação.
Caso seja bem-sucedido, o serviço pode enviar uma mensagem de volta para a mesma fila ou para uma fila de mensagens diferente para que outro serviço possa continuar o fluxo de trabalho, se necessário. Se a operação falhar, o agente de mensagens trabalhará com outros serviços para compensar a operação ou toda a transação.
Problemas e considerações
A descentralização do orquestrador pode causar problemas ao gerenciar o fluxo de trabalho.
Falhas de entrega podem ser um desafio. Os componentes em um aplicativo podem realizar tarefas atômicas, mas ainda podem ter um nível de dependência. A falha em um componente pode afetar outros, o que pode causar atrasos na conclusão da solicitação geral.
Para lidar com falhas normalmente, a implementação de transações de compensação pode introduzir complexidade. A lógica de tratamento de falhas, como transações de compensação, também é propensa a erros.
O padrão é adequado para um fluxo de trabalho em que operações de negócios independentes são processadas em paralelo. O fluxo de trabalho pode se tornar complicado quando a coreografia precisa ocorrer em uma sequência. Por exemplo, o Serviço D poderá iniciar a própria operação somente depois que o Serviço B e o Serviço C tiverem concluído as respectivas operações com êxito.
O padrão se tornará um desafio se o número de serviços crescer rapidamente. Dado o alto número de partes móveis independentes, o fluxo de trabalho entre os serviços tende a ficar complexo. Além disso, o rastreamento distribuído se torna difícil, embora ferramentas como o ServiceInsight junto com o NServiceBus possam ajudar a reduzir esses desafios.
Em um design conduzido por orquestrador, o componente central pode participar parcialmente e delegar a lógica de resiliência a outro componente que repete falhas transitórias, não transitórias e de tempo limite, de forma consistente. Com a dissolução do orquestrador no padrão de coreografia, os componentes downstream não devem pegar essas tarefas de resiliência. Eles ainda deverão ser processados pelo manipulador de resiliência. Mas agora, os componentes downstream deverão se comunicar diretamente com o manipulador de resiliência, aumentando a comunicação ponto a ponto.
Quando usar esse padrão
Use esse padrão quando:
Os componentes downstream lidam com operações atômicas de forma independente. Pense nisso como um mecanismo de "disparar e esquecer". Um componente é responsável por uma tarefa que não precisa ser gerenciada ativamente. Quando a tarefa é concluída, ela envia uma notificação aos outros componentes.
Espera-se que os componentes sejam atualizados e substituídos com frequência. O padrão permite que o aplicativo seja modificado com menos esforço e interrupção mínima dos serviços existentes.
O padrão é um ajuste natural para arquiteturas sem servidor que são apropriadas para fluxos de trabalho simples. Os componentes podem ser de curta duração e orientados a eventos. Quando ocorre um evento, os componentes são iniciados, executam suas tarefas e são removidos assim que a tarefa é concluída.
Esse padrão pode ser uma boa opção para comunicações entre contextos limitados. Para comunicações dentro de um contexto delimitado individual, um padrão orquestrador pode ser considerado.
Há um gargalo de desempenho introduzido pelo orquestrador central.
Esse padrão pode não ser útil quando:
O aplicativo é complexo e requer um componente central para lidar com a lógica compartilhada de modo a manter os componentes downstream leves.
Há situações em que a comunicação ponto a ponto entre os componentes é inevitável.
Você precisa consolidar todas as operações manipuladas por componentes downstream usando a lógica de negócios.
Design de carga de trabalho
Um arquiteto deve avaliar como o padrão de Coreografia pode ser usado no design das suas cargas de trabalho para abordar os objetivos e princípios abordados nos pilares dos Azure Well-Architected Framework. Por exemplo:
Pilar | Como esse padrão apoia os objetivos do pilar |
---|---|
A Excelência Operacional ajuda a fornecer qualidade na carga de trabalho por meio de processos padronizados e coesão da equipe. | Como os componentes distribuídos nesse padrão são autônomos e projetados para serem substituíveis, é possível modificar a carga de trabalho com menos alterações gerais no sistema. - OE:04 Ferramentas e processos |
A eficiência de desempenho ajuda sua carga de trabalho a atender com eficiência às demandas por meio de otimizações em dimensionamento, dados e código. | Esse padrão fornece uma alternativa quando ocorrem gargalos de desempenho em uma topologia de orquestração centralizada. - PE:02 Planejamento de capacidade - PE:05 Dimensionamento e particionamento |
Tal como acontece com qualquer decisão de design, considere quaisquer compensações em relação aos objetivos dos outros pilares que possam ser introduzidos com este padrão.
Exemplo
Este exemplo mostra o padrão de coreografia criando uma carga de trabalho nativa da nuvem orientada a eventos executando funções junto com microsserviços. Quando um cliente solicita que um pacote seja enviado, a carga de trabalho atribui um drone. Assim que o pacote estiver pronto para ser retirado pelo drone agendado, o processo de entrega é iniciado. Enquanto em trânsito, a carga de trabalho lida com a entrega até que ela ganhe o status de envio.
Este exemplo é uma refatoração da implementação Drone Delivery que substitui o padrão Orquestrador pelo padrão Coreografia.
O serviço de ingestão processa as solicitações do cliente e as converte em mensagens, incluindo os detalhes da entrega. As transações comerciais são iniciadas após o consumo dessas novas mensagens.
Uma única transação comercial de cliente requer três operações comerciais distintas:
- Criar ou atualizar um pacote
- Atribuir um drone para entregar o pacote
- Lidar com a entrega que consiste em verificar e, eventualmente, aumentar o reconhecimento quando enviado.
Três microsserviços realizam o processamento de negócios: Pacote, Agendador de Drones e serviços de Entrega. Em vez de um orquestrador central, os serviços usam mensagens para se comunicar entre si. Cada serviço seria responsável por implementar um protocolo com antecedência que coordena de forma descentralizada o fluxo de trabalho da empresa.
Criar
A transação comercial é processada em uma sequência por meio de vários saltos. Cada salto está compartilhando um único barramento de mensagens entre todos os serviços corporativos.
Quando um cliente envia uma solicitação de entrega por meio de um ponto de extremidade HTTP, o serviço Ingestion a recebe, converte essa solicitação em uma mensagem e, em seguida, publica a mensagem no barramento de mensagens compartilhadas. Os serviços empresariais subscritos vão consumir novas mensagens adicionadas ao barramento. Ao receber a mensagem, o serviço de negócios pode concluir a operação com êxito, com falha, ou a solicitação pode atingir o tempo limite. Se tiver êxito, os serviços responderão ao barramento com o código de status Ok, gerará uma nova mensagem de operação e a enviará para o barramento de mensagens. Se houver uma falha ou tempo limite, o serviço relata a falha enviando o código de motivo para o barramento de mensagens. Além disso, a mensagem é adicionada a uma fila de mensagens mortas. As mensagens que não puderam ser recebidas ou processadas dentro de um período de tempo razoável e apropriado também são movidas para o DLQ.
O design usa vários barramentos de mensagens para processar toda a transação comercial. O Barramento de Serviço do Microsoft Azure e e a Grade de Eventos do Microsoft Azure são compostos para fornecer a plataforma de serviço de mensagens para esse design. A carga de trabalho é implantada nos Aplicativos de Contêiner do Azure que hospedam o Azure Functions para ingestão e nos aplicativos que lidam com o processamento orientado a eventos que executa a lógica de negócios.
O design garante que a coreografia ocorra em sequência. Um único namespace do Barramento de Serviço do Azure contém um tópico com duas assinaturas e uma fila com reconhecimento de sessão. O serviço Ingestion publica mensagens para o tópico. O serviço de Pacotes e o serviço Agendador de Drones assinam o tópico e publicam mensagens comunicando o sucesso à fila. A inclusão de um identificador de sessão comum, que é um GUID associado ao identificador de entrega, permite o tratamento ordenado de sequências ilimitadas de mensagens relacionadas. O serviço de Entrega aguarda duas mensagens relacionadas por transação. A primeira mensagem indica que o pacote está pronto para ser enviado e a segunda sinaliza que um drone está programado.
Esse design usa o Barramento de Serviço do Azure para manipular mensagens de alto valor que não podem ser perdidas ou duplicadas durante todo o processo de entrega. Quando o pacote é enviado, ele também publica uma alteração de estado para a Grade de Eventos do Azure. Nesse design, o remetente do evento não tem nenhuma expectativa sobre como a mudança de estado é tratada. Os serviços de organização downstream que não estão incluídos como parte desse design podem estar ouvindo esse tipo de evento e reagir executando uma lógica de propósito comercial específica (ou seja, enviar por e-mail o status do pedido enviado para o usuário).
Se você estiver planejando implantar isso em outro serviço de computação, como AKS o pub-sub pattern application boilplate pode ser implementado com dois contêineres no mesmo pod. Um contêiner executa o embaixador que interage com seu barramento de preferência de mensagens, enquanto o outro executa a lógica de negócios. A abordagem com dois contêineres no mesmo pod melhora o desempenho e a escalabilidade. O embaixador e o serviço de negócios compartilham a mesma rede, permitindo baixa latência e alta taxa de transferência.
Para evitar operações de repetição em cascata que podem levar a vários esforços, os serviços corporativos devem sinalizar imediatamente mensagens inaceitáveis. É possível enriquecer essas mensagens usando códigos de motivo conhecidos ou um código de aplicativo definido, para que ele possa ser movido para uma fila de letras mortas (DLQ). Considere gerenciar problemas de consistência implementando o Saga a partir de serviços downstream. Por exemplo, outro serviço poderia lidar com mensagens com letras mortas para fins de correção apenas executando uma transação de compensação, nova tentativa ou pivot.
Os serviços empresariais são idempotentes para garantir que as novas tentativas de operações não resultem em recursos duplicados. Por exemplo, o serviço Pacote usa operações upsert para adicionar dados ao armazenamento de dados.
Recursos relacionados
Considere estes padrões em seu design para coreografia.
Modularize o serviço de negócios usando o padrão de design do embaixador.
Implemente o padrão de nivelamento de carga baseado em fila para lidar com picos da carga de trabalho.
Use mensagens distribuídas assíncronas por meio do padrão publicador-assinante.
Use transações compensatórias para desfazer uma série de operações bem-sucedidas no caso de uma ou mais operações relacionadas falharem.
Para obter informações sobre como usar um agente de mensagens em uma infraestrutura de mensagens, confira Opções de mensagens assíncronas no Azure.