Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
As infraestruturas baseadas na nuvem, como o Armazenamento do Azure, fornecem uma plataforma altamente disponível e durável para hospedar dados e aplicativos. Os desenvolvedores de aplicativos baseados em nuvem devem considerar cuidadosamente como aproveitar essa plataforma para maximizar essas vantagens para seus usuários. O Armazenamento do Azure oferece opções de redundância geográfica para garantir alta disponibilidade mesmo durante uma interrupção regional. As contas de armazenamento configuradas para replicação com redundância geográfica são replicadas de forma síncrona na região primária e de forma assíncrona para uma região secundária a centenas de quilômetros de distância.
O Armazenamento do Azure oferece duas opções para replicação com redundância geográfica: armazenamento com redundância geográfica (GRS) e armazenamento com redundância de zona geográfica (GZRS). Para utilizar as opções de redundância geográfica do Armazenamento do Azure, certifique-se de que a sua conta de armazenamento está configurada para armazenamento com redundância geográfica de acesso de leitura (RA-GRS) ou armazenamento com redundância de zona geográfica de acesso de leitura (RA-GZRS). Se não estiver, saiba mais sobre como alterar o tipo de replicação da conta de armazenamento.
Este artigo mostra como projetar um aplicativo que continuará a funcionar, embora em uma capacidade limitada, mesmo quando houver uma interrupção significativa na região primária. Se a região primária ficar indisponível, seu aplicativo poderá alternar perfeitamente para executar operações de leitura na região secundária até que a região primária responda novamente.
Considerações sobre o design do aplicativo
Você pode projetar seu aplicativo para lidar com falhas transitórias ou interrupções significativas lendo da região secundária quando houver um problema que interfira com a leitura da região primária. Quando a região primária estiver disponível novamente, seu aplicativo poderá retornar à leitura da região primária.
Tenha em mente estas principais considerações ao projetar seu aplicativo para disponibilidade e resiliência usando RA-GRS ou RA-GZRS:
Uma cópia somente leitura dos dados armazenados na região primária é replicada de forma assíncrona em uma região secundária. Essa replicação assíncrona significa que a cópia somente leitura na região secundária é eventualmente consistente com os dados na região primária. O serviço de armazenamento determina a localização da região secundária.
Você pode usar as bibliotecas de cliente do Armazenamento do Azure para executar solicitações de leitura e atualização no ponto de extremidade da região primária. Se a região primária não estiver disponível, você poderá redirecionar automaticamente as solicitações de leitura para a região secundária. Você também pode configurar seu aplicativo para enviar solicitações de leitura diretamente para a região secundária, se desejar, mesmo quando a região primária estiver disponível.
Se a região primária ficar indisponível, você poderá iniciar um failover de conta. Quando você faz failover para a região secundária, as entradas DNS que apontam para a região primária são alteradas para apontar para a região secundária. Após a conclusão do failover, o acesso de escrita é restaurado para as contas GRS e RA-GRS. Para mais informações, consulte Recuperação de desastres e failover da conta de armazenamento.
Trabalhando com dados eventualmente consistentes
A solução proposta pressupõe que é aceitável retornar dados potencialmente obsoletos para o aplicativo de chamada. Como os dados na região secundária são eventualmente consistentes, é possível que a região primária fique inacessível antes que uma atualização para a região secundária termine de replicar.
Por exemplo, suponha que seu cliente envie uma atualização com êxito, mas a região primária falhe antes que a atualização seja propagada para a região secundária. Quando o cliente pede para ler os dados de volta, ele recebe os dados obsoletos da região secundária em vez dos dados atualizados. Ao projetar seu aplicativo, você deve decidir se esse comportamento é aceitável ou não. Se for, você também precisa considerar como notificar o usuário.
Mais adiante neste artigo, você aprenderá mais sobre como lidar com dados eventualmente consistentes e como verificar a propriedade Last Sync Time para avaliar quaisquer discrepâncias entre dados nas regiões primária e secundária.
Gerir serviços de forma separada ou todos em conjunto
Embora improvável, é possível que um serviço (blobs, filas, tabelas ou arquivos) fique indisponível enquanto os outros serviços ainda estão totalmente funcionais. Você pode lidar com as novas tentativas para cada serviço separadamente ou pode lidar com repetições genericamente para todos os serviços de armazenamento juntos.
Por exemplo, se você usar filas e blobs em seu aplicativo, você pode decidir colocar um código separado para lidar com erros que podem ser repetidos para cada serviço. Dessa forma, um erro de serviço de blob afetará apenas a parte do seu aplicativo que lida com blobs, deixando as filas para continuar funcionando normalmente. Se, no entanto, você decidir lidar com todas as novas tentativas de serviço de armazenamento juntas, as solicitações para os serviços de blob e fila serão afetadas se qualquer um dos serviços retornar um erro que possa ser repetido.
Em última análise, esta decisão depende da complexidade do seu pedido. Você pode preferir lidar com falhas por serviço para limitar o impacto das tentativas. Ou você pode decidir redirecionar as solicitações de leitura de todos os serviços de armazenamento para a região secundária quando detetar um problema com qualquer serviço de armazenamento na região primária.
Executando seu aplicativo no modo somente leitura
Para se preparar efetivamente para uma interrupção na região primária, seu aplicativo deve ser capaz de lidar com solicitações de leitura com falha e solicitações de atualização com falha. Se a região primária falhar, as solicitações de leitura poderão ser redirecionadas para a região secundária. No entanto, as solicitações de atualização não podem ser redirecionadas porque os dados replicados na região secundária são somente leitura. Por esse motivo, você precisa projetar seu aplicativo para poder ser executado no modo somente leitura.
Por exemplo, você pode definir um sinalizador que é verificado antes que quaisquer solicitações de atualização sejam enviadas ao Armazenamento do Azure. Quando uma solicitação de atualização é recebida, você pode ignorá-la e retornar uma resposta apropriada ao usuário. Você pode até mesmo optar por desativar determinados recursos completamente até que o problema seja resolvido e notificar os usuários de que os recursos estão temporariamente indisponíveis.
Se você decidir manipular erros para cada serviço separadamente, também precisará lidar com a capacidade de executar seu aplicativo no modo somente leitura por serviço. Por exemplo, você pode configurar sinalizadores somente leitura para cada serviço. Em seguida, você pode ativar ou desativar os sinalizadores no código, conforme necessário.
Ser capaz de executar seu aplicativo no modo somente leitura também oferece a capacidade de garantir funcionalidade limitada durante uma grande atualização do aplicativo. Você pode acionar seu aplicativo para ser executado no modo somente leitura e apontar para o data center secundário, garantindo que ninguém esteja acessando os dados na região primária enquanto você está fazendo atualizações.
Lidar com atualizações ao executar no modo somente leitura
Há muitas maneiras de lidar com solicitações de atualização ao executar no modo somente leitura. Esta seção se concentra em alguns padrões gerais a serem considerados.
Você pode responder ao usuário e notificá-lo de que as solicitações de atualização não estão sendo processadas no momento. Por exemplo, um sistema de gerenciamento de contatos pode permitir que os usuários acessem informações de contato, mas não façam atualizações.
Você pode enfileirar suas atualizações em outra região. Nesse caso, você escreveria suas solicitações de atualização pendentes em uma fila em uma região diferente e, em seguida, processaria essas solicitações depois que o data center principal ficar online novamente. Nesse cenário, você deve informar ao usuário que a solicitação de atualização está na fila para processamento posterior.
Você pode gravar suas atualizações em uma conta de armazenamento em outra região. Quando a região primária voltar a ficar online, você poderá mesclar essas atualizações nos dados primários, dependendo da estrutura dos dados. Por exemplo, se você estiver criando arquivos separados com um carimbo de data/hora no nome, poderá copiar esses arquivos de volta para a região primária. Essa solução pode ser aplicada a cargas de trabalho, como registro em log e dados de IoT.
Tratamento de novas tentativas
Os aplicativos que se comunicam com serviços executados na nuvem devem ser sensíveis a eventos não planejados e falhas que possam ocorrer. Essas falhas podem ser transitórias ou persistentes, variando de uma perda momentânea de conectividade a uma interrupção significativa devido a um desastre natural. É importante projetar aplicativos na nuvem com tratamento de repetição apropriado para maximizar a disponibilidade e melhorar a estabilidade geral do aplicativo.
Pedidos de leitura
Se a região primária ficar indisponível, as solicitações de leitura poderão ser redirecionadas para o armazenamento secundário. Como observado anteriormente, deve ser aceitável que seu aplicativo leia dados potencialmente obsoletos. A biblioteca de cliente do Armazenamento do Azure oferece opções para lidar com novas tentativas e redirecionar solicitações de leitura para uma região secundária.
Neste exemplo, a manipulação de novas tentativas para armazenamento de Blob é configurada BlobClientOptions
na classe e será aplicada ao BlobServiceClient
objeto que criamos usando essas opções de configuração. Essa configuração é uma abordagem primária e secundária , na qual as novas tentativas de solicitação de leitura da região primária são redirecionadas para a região secundária. Esta abordagem é melhor quando se espera que as falhas na região primária sejam temporárias.
string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");
// Provide the client configuration options for connecting to Azure Blob storage
BlobClientOptions blobClientOptions = new BlobClientOptions()
{
Retry = {
// The delay between retry attempts for a fixed approach or the delay
// on which to base calculations for a backoff-based approach
Delay = TimeSpan.FromSeconds(2),
// The maximum number of retry attempts before giving up
MaxRetries = 5,
// The approach to use for calculating retry delays
Mode = RetryMode.Exponential,
// The maximum permissible delay between retry attempts
MaxDelay = TimeSpan.FromSeconds(10)
},
// If the GeoRedundantSecondaryUri property is set, the secondary Uri will be used for
// GET or HEAD requests during retries.
// If the status of the response from the secondary Uri is a 404, then subsequent retries
// for the request will not use the secondary Uri again, as this indicates that the resource
// may not have propagated there yet.
// Otherwise, subsequent retries will alternate back and forth between primary and secondary Uri.
GeoRedundantSecondaryUri = secondaryAccountUri
};
// Create a BlobServiceClient object using the configuration options above
BlobServiceClient blobServiceClient = new BlobServiceClient(primaryAccountUri, new DefaultAzureCredential(), blobClientOptions);
Se você determinar que a região primária provavelmente ficará indisponível por um longo período de tempo, poderá configurar todas as solicitações de leitura para apontar para a região secundária. Esta configuração é uma abordagem secundária . Como discutido anteriormente, você precisará de uma estratégia para lidar com solicitações de atualização durante esse período e uma maneira de informar aos usuários que apenas as solicitações de leitura estão sendo processadas. Neste exemplo, criamos uma nova instância da qual usa o ponto de extremidade da BlobServiceClient
região secundária.
string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");
// Create a BlobServiceClient object pointed at the secondary Uri
// Use blobServiceClientSecondary only when issuing read requests, as secondary storage is read-only
BlobServiceClient blobServiceClientSecondary = new BlobServiceClient(secondaryAccountUri, new DefaultAzureCredential(), blobClientOptions);
Saber quando mudar para o modo somente leitura e somente solicitações secundárias faz parte de um padrão de projeto arquitetônico chamado padrão Disjuntor, que será discutido em uma seção posterior.
Pedidos de atualização
As solicitações de atualização não podem ser redirecionadas para o armazenamento secundário, que é somente leitura. Conforme descrito anteriormente, seu aplicativo precisa ser capaz de lidar com solicitações de atualização quando a região primária não estiver disponível.
O padrão Disjuntor também pode ser aplicado a solicitações de atualização. Para lidar com erros de solicitação de atualização, você pode definir um limite no código, como 10 falhas consecutivas, e controlar o número de falhas de solicitações para a região primária. Depois que o limite for atingido, você poderá alternar o aplicativo para o modo somente leitura para que as solicitações de atualização para a região primária não sejam mais emitidas.
Como implementar o padrão Circuit Breaker
O tratamento de falhas que podem levar um tempo variável para serem recuperadas faz parte de um padrão de projeto arquitetônico chamado padrão de disjuntor. A implementação adequada desse padrão pode impedir que um aplicativo tente executar repetidamente uma operação que provavelmente falhará, melhorando assim a estabilidade e a resiliência do aplicativo.
Um aspeto do padrão de disjuntor é identificar quando há um problema contínuo com um ponto de extremidade primário. Para fazer essa determinação, você pode monitorar a frequência com que o cliente encontra erros que podem ser repetidos. Como cada cenário é diferente, você precisa determinar um limite apropriado a ser usado para a decisão de alternar para o ponto de extremidade secundário e executar o aplicativo no modo somente leitura.
Por exemplo, você pode decidir executar o switch se houver 10 falhas consecutivas na região primária. Você pode acompanhar isso mantendo uma contagem das falhas no código. Se houver um sucesso antes de atingir o limite, defina a contagem de volta para zero. Se a contagem atingir o limite, alterne o aplicativo para usar a região secundária para solicitações de leitura.
Como uma abordagem alternativa, você pode decidir implementar um componente de monitoramento personalizado em seu aplicativo. Esse componente pode executar continuamente ping no endpoint de armazenamento principal com solicitações de leitura triviais (como a leitura de um pequeno blob) para determinar sua integridade. Esta abordagem absorveria alguns recursos, mas não um montante significativo. Quando for descoberto um problema que atinja o limite, você alternará para solicitações de leitura secundárias e somente leitura. Nesse cenário, quando o ping do ponto de extremidade de armazenamento primário for bem-sucedido novamente, você poderá voltar para a região principal e continuar permitindo atualizações.
O limite de erro usado para determinar quando fazer a mudança pode variar de serviço para serviço dentro do seu aplicativo, portanto, você deve considerar torná-los parâmetros configuráveis.
Outra consideração é como lidar com várias instâncias de um aplicativo e o que fazer quando você deteta erros repetidos em cada instância. Por exemplo, você pode ter 20 VMs em execução com o mesmo aplicativo carregado. Você lida com cada instância separadamente? Se uma instância começar a ter problemas, você deseja limitar a resposta a apenas uma instância? Ou você deseja que todas as instâncias respondam da mesma maneira quando uma instância tem um problema? Lidar com as instâncias separadamente é muito mais simples do que tentar coordenar a resposta entre elas, mas sua abordagem dependerá da arquitetura do seu aplicativo.
Tratamento de dados eventualmente consistentes
O armazenamento com redundância geográfica funciona replicando transações da região primária para a secundária. O processo de replicação garante que os dados na região secundária sejam eventualmente consistentes. Isso significa que todas as transações na região primária acabarão aparecendo na região secundária, mas que pode haver um atraso antes de aparecerem. Também não há garantia de que as transações chegarão à região secundária na mesma ordem em que foram aplicadas originalmente na região primária. Se suas transações chegarem na região secundária fora de ordem, você poderá considerar que seus dados na região secundária estão em um estado inconsistente até que o serviço se recupere.
O exemplo a seguir para o armazenamento de tabela do Azure mostra o que pode acontecer quando você atualiza os detalhes de um funcionário para torná-lo um membro da função de administrador. Para este exemplo, isso requer que você atualize a entidade de funcionário e atualize uma entidade de função de administrador com uma contagem do número total de administradores. Observe como as atualizações são aplicadas fora de ordem na região secundária.
Tempo | Transação | Replicação | Hora da última sincronização | Resultado |
---|---|---|---|---|
T0 | Transação A: Inserir funcionário entidade em primário |
Transação A inserida no primário ainda não foi replicado. |
||
T1 | Transação A replicado para secundário |
T1 | A transação A foi replicada para o sistema secundário. Hora da última sincronização atualizada. |
|
T2 | Transação B: Atualização entidade de funcionários no ensino primário |
T1 | Transação B escrita no servidor primário ainda não foi replicado. |
|
T3 | Transação C: Atualizar administrador papel de entidade em primário |
T1 | Transação C gravada no sistema primário, ainda não foi replicado. |
|
T4 | Transação C replicado para secundário |
T1 | Transação C replicada no sistema secundário. LastSyncTime não foi atualizado devido a a transação B ainda não foi replicada. |
|
T5 | Ler entidades do secundário |
T1 | Você obtém o valor obsoleto para o funcionário porque a transação B ainda não replicado ainda. Você obtém o novo valor para entidade de função de administrador porque C tem replicado. O Último Tempo de Sincronização ainda não chegou foi atualizada porque a transação B não foi replicado. Você pode dizer a diferença A entidade de função de administrador é inconsistente porque a data/hora da entidade é posterior a Última Hora de Sincronização. |
|
T6 | Transação B replicado para secundário |
T6 |
T6 – Todas as transações através de C têm foi replicado, Hora da última sincronização é atualizado. |
Neste exemplo, suponha que o cliente alterna para leitura da região secundária em T5. Ele pode ler com êxito a entidade de função de administrador no momento, mas a entidade contém um valor para a contagem de administradores que não é consistente com o número de entidades de funcionários marcadas como administradores na região secundária no momento. Seu cliente pode exibir esse valor, com o risco de que as informações sejam inconsistentes. Como alternativa, o cliente pode tentar determinar que a função de administrador está em um estado potencialmente inconsistente porque as atualizações aconteceram fora de ordem e, em seguida, informar o usuário sobre esse fato.
Para determinar se uma conta de armazenamento tem dados potencialmente inconsistentes, o cliente pode verificar o valor da propriedade Last Sync Time . A Última Hora de Sincronização informa a hora em que os dados na região secundária foram consistentes pela última vez e quando o serviço aplicou todas as transações antes desse point-in-time. No exemplo mostrado acima, depois que o serviço insere a entidade do funcionário na região secundária, o último tempo de sincronização é definido como T1. Permanece em T1 até que o serviço atualize a entidade empregada na região secundária quando estiver definido para T6. Se o cliente recuperar a última hora de sincronização ao ler a entidade em T5, o cliente poderá compará-la com o carimbo de data/hora na entidade. Se o carimbo de data/hora na entidade for posterior à última hora de sincronização, a entidade estará em um estado potencialmente inconsistente e você poderá tomar as medidas apropriadas. O uso deste campo requer que você saiba quando a última atualização do primário foi concluída.
Para saber como verificar a última hora de sincronização, consulte Verificar a propriedade Última hora de sincronização de uma conta de armazenamento.
Testes
É importante testar se seu aplicativo se comporta como esperado quando encontra erros que podem ser repetidos. Por exemplo, você precisa testar se o aplicativo alterna para a região secundária quando deteta um problema e, em seguida, alterna novamente quando a região primária fica disponível novamente. Para testar corretamente esse comportamento, você precisa de uma maneira de simular erros repetidos e controlar a frequência com que eles ocorrem.
Uma opção é usar o Fiddler para intercetar e modificar respostas HTTP em um script. Esse script pode identificar respostas que vêm do seu ponto de extremidade principal e alterar o código de status HTTP para um que a biblioteca do cliente de armazenamento reconhece como um erro que pode ser repetido. Este trecho de código mostra um exemplo simples de um script do Fiddler que interceta respostas a solicitações de leitura na tabela employeedata para retornar um status 502:
static function OnBeforeResponse(oSession: Session) {
...
if ((oSession.hostname == "\[YOURSTORAGEACCOUNTNAME\].table.core.windows.net")
&& (oSession.PathAndQuery.StartsWith("/employeedata?$filter"))) {
oSession.responseCode = 502;
}
}
Você pode estender este exemplo para intercetar uma gama mais ampla de solicitações e alterar apenas o responseCode em algumas delas para simular melhor um cenário do mundo real. Para obter mais informações sobre como personalizar scripts do Fiddler, consulte Modificando uma solicitação ou resposta na documentação do Fiddler.
Se você tiver configurado limites configuráveis para alternar seu aplicativo para somente leitura, será mais fácil testar o comportamento com volumes de transações que não sejam de produção.
Próximos passos
Para obter um exemplo completo que mostra como alternar entre os endpoints principais e secundários, consulte Exemplos do Azure – Utilizando o padrão de "Circuit Breaker" com armazenamento RA-GRS.