Partilhar via


Padrões de tarefas de replicação de mensagens

A visão geral da federação e a visão geral das funções do replicador explicam a lógica e os elementos básicos das tarefas de replicação, e é recomendável que você se familiarize com eles antes de continuar com este artigo.

Neste artigo, detalhamos as diretrizes de implementação para vários dos padrões destacados na seção de visão geral.

Replicação

O padrão de replicação copia mensagens de uma fila ou tópico para o próximo, ou de uma fila ou tópico para algum outro destino, como um Hub de Eventos. As mensagens são encaminhadas sem fazer modificações na carga útil da mensagem.

A implementação desse padrão é coberta pelo exemplo de replicação de mensagens de e para o Barramento de Serviço do Azure.

Preservação de sequências e ordens

O modelo de replicação não visa preservar a ordem absoluta das mensagens de uma fila ou tópico de origem em uma fila ou tópico de destino, mas se concentra, sempre que necessário, em preservar a ordem relativa das mensagens onde o aplicativo o exige. O aplicativo permite isso habilitando o suporte de sessão para a entidade de origem e agrupando mensagens relacionadas com a mesma chave de sessão.

As funções de replicação pré-criadas com reconhecimento de sessão garantem que as sequências de mensagens com a mesma ID de sessão recuperada de uma entidade de origem sejam enviadas para a fila ou tópico de destino como um lote na sequência original e com a mesma ID de sessão.

Metadados atribuídos ao serviço

Os metadados atribuídos ao serviço de uma mensagem obtida da fila ou tópico de origem, o tempo de fila original e o número de sequência, são substituídos por novos valores atribuídos ao serviço na fila ou tópico de destino, mas com as tarefas de replicação padrão fornecidas em nossos exemplos, os valores originais são preservados nas propriedades do usuário: repl-enqueue-time (ISO8601 string) e repl-sequence.

Essas propriedades são do tipo string e contêm o valor stringified das respetivas propriedades originais. Se a mensagem for encaminhada várias vezes, os metadados atribuídos ao serviço da origem imediata serão acrescentados às propriedades já existentes, com valores separados por ponto-e-vírgula.

Ativação pós-falha

Se você estiver usando a replicação para fins de recuperação de desastres, para proteção contra mensagens de disponibilidade regional no serviço Service Bus ou contra interrupções de rede, qualquer cenário de falha exigirá a execução de um failover de uma fila ou tópico para o próximo, dizendo aos produtores e/ou consumidores para usar o ponto de extremidade secundário.

Para todos os cenários de failover, supõe-se que os elementos necessários dos namespaces são estruturalmente idênticos, o que significa que filas e tópicos são nomeados de forma idêntica e que as regras de assinatura de acesso compartilhado e/ou regras de controle de acesso baseadas em função são configuradas da mesma maneira. Você pode criar (e atualizar) um namespace secundário seguindo as orientações para mover namespaces e omitir a etapa de limpeza.

Para forçar produtores e consumidores a mudar, você precisa disponibilizar as informações sobre qual namespace usar para pesquisa em um local fácil de acessar e atualizar. Se os produtores ou consumidores se depararem com erros frequentes ou persistentes, devem consultar esse local e ajustar a sua configuração. Existem várias maneiras de compartilhar essa configuração, mas destacamos duas a seguir: DNS e compartilhamentos de arquivos.

Configuração de failover baseada em DNS

Uma abordagem candidata é manter as informações nos registros SRV DNS em um DNS que você controla e apontar para os respetivos pontos de extremidade de fila ou tópico. Lembre-se de que os Hubs de mensagens não permitem que seus pontos de extremidade sejam diretamente aliados com registros CNAME, o que significa que você usará o DNS como um mecanismo de pesquisa resiliente para endereços de ponto de extremidade e não para resolver diretamente as informações de endereço IP.

Suponha que você é o proprietário do domínio example.com e, para seu aplicativo, de uma zona test.example.com. Para dois Service Bus alternativos, você criará mais duas zonas aninhadas e um registro SRV em cada uma.

Os registros SRV são, seguindo convenção comum, prefixados e _azure_servicebus._amqp mantêm dois registros de ponto de extremidade: um para AMQP-over-TLS na porta 5671 e outro para AMQP-over-WebSockets na porta 443, ambos apontando para o ponto de extremidade do Service Bus do namespace correspondente à zona.

Zona Registo SRV
sb1.test.example.com _azure_servicebus._amqp.sb1.test.example.com
1 1 5671 sb1-test-example-com.servicebus.windows.net
2 2 443 sb1-test-example-com.servicebus.windows.net
sb2.test.example.com _azure_servicebus._amqp.sb1.test.example.com
1 1 5671 sb2-test-example-com.servicebus.windows.net
2 2 443 sb2-test-example-com.servicebus.windows.net

Na zona do seu aplicativo, você criará uma entrada CNAME que aponta para a zona subordinada correspondente à sua fila ou tópico principal:

Registo CNAME Alias
servicebus.test.example.com sb1.test.example.com

Usando um cliente DNS que permite consultar registros CNAME e SRV explicitamente (os clientes internos de Java e .NET só permitem a resolução simples de nomes para endereços IP), você pode resolver o ponto de extremidade desejado. Com DnsClient.NET, por exemplo, a função de pesquisa é:

static string GetServiceBusName(string aliasName)
{
    const string SrvRecordPrefix = "_azure_servicebus._amqp.";
    LookupClient lookup = new LookupClient();

    return (from CNameRecord alias in (lookup.Query(aliasName, QueryType.CNAME).Answers)
            from SrvRecord srv in lookup.Query(SrvRecordPrefix + alias.CanonicalName, QueryType.SRV).Answers
            where srv.Port == 5671
            select srv.Target).FirstOrDefault()?.Value.TrimEnd('.');
}

A função retorna o nome do host de destino registrado para a porta 5671 da zona atualmente aliased com o CNAME, como mostrado acima.

Executar um failover requer editar o registro CNAME e apontá-lo para a zona alternativa.

A vantagem de usar o DNS, e especificamente o DNS do Azure, é que as informações do DNS do Azure são replicadas globalmente e, portanto, resilientes contra interrupções de uma única região.

Este procedimento é semelhante ao funcionamento do Service Bus Geo-DR , mas totalmente sob seu próprio controle e também funciona com cenários ativos/ativos.

Configuração de failover baseada em compartilhamento de arquivos

A alternativa mais simples ao uso do DNS para compartilhar informações de ponto de extremidade é colocar o nome do ponto de extremidade primário em um arquivo de texto sem formatação e servir o arquivo a partir de uma infraestrutura robusta contra interrupções e que ainda permita atualizações.

Se você já executa uma infraestrutura de site altamente disponível com disponibilidade global e replicação de conteúdo, adicione esse arquivo lá e publique novamente o arquivo se uma opção for necessária.

Unir

O padrão de mesclagem tem uma ou mais tarefas de replicação apontando para um destino, possivelmente simultaneamente com produtores regulares também enviando mensagens para o mesmo destino.

As variações deste padrão são:

  • Duas ou mais funções de replicação simultaneamente adquirindo mensagens de fontes separadas e enviando-as para o mesmo destino.
  • Mais uma função de replicação que adquire mensagens de uma fonte, enquanto o destino também é usado diretamente pelos produtores.
  • O padrão anterior, mas mensagens espelhadas entre dois ou mais tópicos, resultando nesses tópicos contendo as mesmas mensagens, não importa onde as mensagens são produzidas.

As duas primeiras variações de padrão são triviais e não diferem das tarefas de replicação simples.

O último cenário requer a exclusão de mensagens já replicadas de serem replicadas novamente. A técnica é demonstrada e explicada na amostra ativa/ativa.

Editor

O padrão do editor se baseia no padrão de replicação, mas as mensagens são modificadas antes de serem encaminhadas. Exemplos de tais modificações são:

  • Transcodificação - Se o conteúdo da mensagem (também referido como "corpo" ou "carga útil") chegar do código-fonte codificado usando o formato Apache Avro ou algum formato de serialização proprietário, mas a expectativa do sistema proprietário do destino é que o conteúdo seja codificado JSON, uma tarefa de replicação de transcodificação primeiro desserializará a carga do Apache Avro em um gráfico de objeto na memória e, em seguida, serializará esse gráfico no JSON para a mensagem que está sendo encaminhada. A transcodificação também inclui tarefas de compressão e descompressão de conteúdo.
  • Transformação - as mensagens que contêm dados estruturados podem exigir a reformulação desses dados para facilitar o consumo pelos consumidores a jusante. Isso pode envolver trabalhos como o nivelamento de estruturas aninhadas, a remoção de elementos de dados estranhos ou a reformulação da carga útil para se ajustar exatamente a um determinado esquema.
  • Lote - as mensagens podem ser recebidas em lotes (várias mensagens em uma única transferência) de uma fonte, mas precisam ser encaminhadas individualmente para um destino, ou vice-versa. Uma tarefa pode, portanto, encaminhar várias mensagens com base em uma única transferência de mensagem de entrada ou agregar um conjunto de mensagens que são transferidas juntas.
  • Validação - os dados de mensagens de fontes externas precisam frequentemente de ser verificados para verificar se estão em conformidade com um conjunto de regras antes de poderem ser encaminhados. As regras podem ser expressas usando esquemas ou código. As mensagens que não estão em conformidade podem ser descartadas, com o problema anotado nos logs, ou podem ser encaminhadas para um destino de destino especial para lidar com elas ainda mais.
  • Enriquecimento - os dados de mensagens provenientes de algumas fontes podem exigir enriquecimento com contexto adicional para que possam ser utilizados em sistemas de destino. Isso pode envolver a pesquisa de dados de referência e a incorporação desses dados com a mensagem ou a adição de informações sobre a fonte conhecida na tarefa de replicação, mas não contida nas mensagens.
  • Filtragem - Algumas mensagens que chegam de uma fonte podem ter que ser retidas do destino com base em alguma regra. Um filtro testa a mensagem em relação a uma regra e descarta a mensagem se ela não corresponder à regra. Filtrar mensagens duplicadas observando certos critérios e descartar mensagens subsequentes com os mesmos valores é uma forma de filtragem.
  • Roteamento e particionamento - Algumas tarefas de replicação podem permitir dois ou mais destinos alternativos e definir regras para as quais o destino de replicação é escolhido para qualquer mensagem específica com base nos metadados ou no conteúdo da mensagem. Uma forma especial de roteamento é o particionamento, em que a tarefa atribui explicitamente partições em um destino de replicação com base em regras.
  • Criptografia - Uma tarefa de replicação pode ter que descriptografar o conteúdo que chega da fonte e/ou criptografar o conteúdo encaminhado para um destino e/ou pode ter que verificar a integridade do conteúdo e dos metadados relativos a uma assinatura carregada na mensagem, ou anexar tal assinatura.
  • Atestado - Uma tarefa de replicação pode anexar metadados, potencialmente protegidos por uma assinatura digital, a uma mensagem que ateste que a mensagem foi recebida através de um canal específico ou em um momento específico.
  • Encadeamento - Uma tarefa de replicação pode aplicar assinaturas a sequências de mensagens de modo que a integridade da sequência seja protegida e as mensagens ausentes possam ser detetadas.

Todos esses padrões podem ser implementados usando o Azure Functions, usando o Gatilho de Hubs de mensagem para adquirir mensagens e a vinculação de saída de fila ou tópico para entregá-las.

Encaminhamento

O padrão de roteamento se baseia no padrão de replicação , mas em vez de ter uma origem e um destino, a tarefa de replicação tem vários destinos, ilustrados aqui em C#:

[FunctionName("SBRouter")]
public static async Task Run(
    [ServiceBusTrigger("source", Connection = "serviceBusConnectionAppSetting")] ServiceBusReceivedMessage[] messages,
    [ServiceBusOutput("dest1", Connection = "serviceBusConnectionAppSetting")] IAsyncCollector<dynamic> output1,
    [ServiceBusOutput("dest2", Connection = "serviceBusConnectionAppSetting")] IAsyncCollector<dynamic> output2,
    ILogger log)
{
    foreach (Message messageData in messages)
    {
        // send to output1 or output2 based on criteria 
    }
}

A função de roteamento considerará os metadados da mensagem e/ou a carga útil da mensagem e, em seguida, escolherá um dos destinos disponíveis para enviar.

Próximos passos