Operações do Gridwich para Armazenamento do Microsoft Azure

Armazenamento do Azure

O Serviço de Armazenamento do Microsoft Azure do Gridwich, Gridwich.SagaParticipants.Storage.AzureStorage, fornece operações de blob e contêiner para contas de armazenamento do Azure configuradas para Gridwich. As operações de armazenamento de exemplo são Criar blob, Excluir contêiner, Copiar blob ou Alterar a camada de armazenamento.

O Gridwich exige que os próprios mecanismos de armazenamento funcionem para blobs de blocos e contêineres do Armazenamento do Microsoft Azure. Com classes distintas e operações do Serviço de Armazenamento para blobs e contêineres, não há ambiguidade sobre se uma determinada operação de armazenamento está relacionada a um blob ou a um contêiner. Este artigo se aplica a blobs e contêineres, exceto quando observado.

O Gridwich expõe a maioria das operações de armazenamento para sistemas externos dentro do Storage.AzureStorageparticipante da saga. Outros participantes da saga usam o serviço de armazenamento para tarefas como copiar blobs entre diferentes contêineres ou contas quando configuram fluxos de trabalho de codificação.

Este artigo descreve como o Serviço de Armazenamento do Microsoft Azure do Gridwich atende aos requisitos da solução e se integra a mecanismos como manipuladores de eventos. Os links apontam para o código-fonte correspondente, que contém comentários mais extensos sobre os contêineres, classes e mecanismos.

SDK do Armazenamento do Microsoft Azure

O Gridwich usa classes do SDK de Armazenamento do Microsoft Azure para interagir com o Armazenamento do Azure, em vez de criar solicitações REST. No provedor de armazenamento, as classes SDK BlobBaseClient e BlobContainerClient gerenciam solicitações de armazenamento.

Atualmente, essas classes de cliente de SDK permitem apenas o acesso indireto aos dois cabeçalhos HTTP que o Gridwich precisa manipular, x-ms-client-request-id para o contexto de operação e ETag para a versão do objeto.

Em Gridwich, um par de classes de provedor dispensa a funcionalidade BlobBaseClientProvider e BlobContainerClientProvider em unidades chamadas sleeves. Para obter detalhes sobre sleeves, confira Sleeves de armazenamento.

O diagrama a seguir ilustra a estrutura das classes SDK e Gridwich e como as instâncias se relacionam entre si. As setas indicam "tem uma referência a".

Diagram showing client object instance relationships between the Storage SDK classes.

Política de pipeline

Você define o gancho para manipular os cabeçalhos HTTP como uma instância de política de pipeline ao criar a instância do cliente. Você pode definir essa política somente no momento da criação da instância do cliente e não pode alterar a política. O código do provedor de armazenamento que usa o cliente precisa ser capaz de manipular os valores de cabeçalho durante a execução. O desafio é fazer com que o provedor de armazenamento e o pipeline interajam de maneira limpa.

Para a política de pipeline de Gridwich, consulte a classe BlobClientPipelinePolicy .

Cache do Serviço de Armazenamento

O estabelecimento e a autenticação de conexões TCP criam sobrecarga quando uma instância de objeto cliente do SDK envia sua primeira solicitação para o Armazenamento do Microsoft Azure. Várias chamadas para o mesmo blob em uma solicitação do sistema externo, por exemplo, Get Metadata e, em seguida, Delete blob, compõem a sobrecarga.

Para atenuar a sobrecarga, o Gridwich mantém um cache de uma instância de cliente para cada blob de armazenamento ou contêiner, dependendo das classes do SDK que o contexto de operação usa. O Gridwich retém essa instância de cliente e pode usar a instância para várias operações de Armazenamento do Microsoft Azure no mesmo blob ou contêiner durante uma solicitação do sistema externo.

As classes de cliente fornecidas pelo SDK do Azure exigem que as instâncias de objeto cliente do SDK sejam específicas para um blob ou contêiner específico no momento da criação. Também não há garantia de que as instâncias sejam seguras para uso simultâneo em threads diferentes. Como um contexto de operação representa apenas uma solicitação, o Gridwich baseia o cache na combinação do nome do blob ou contêiner com contexto de operação.

Essa reutilização de instância, combinada com a estrutura do cliente do SDK de Armazenamento do Microsoft Azure, requer código de suporte adicional para equilibrar a eficiência e a clareza de código.

Argumento de contexto

Quase todas as operações do Serviço de Armazenamento Gridwich exigem um argumento de contexto especial do tipo StorageClientProviderContext. Esse argumento de contexto atende aos seguintes requisitos:

  • Fornece ao sistema externo respostas, que incluem o valor de contexto de operação baseado em JSON exclusivo por solicitação especificado pelo sistema externo na solicitação do Gridwich. Para obter mais informações, confira Contexto de operação.

  • Permite que chamadores do Serviço de Armazenamento, como manipuladores de eventos do Gridwich, controlem quais respostas são visíveis para o sistema externo. Esse controle impede que o serviço sobrecarregue o sistema externo com eventos de notificação irrelevantes. Para obter mais informações, confira Silenciamento de contexto.

  • Está em conformidade com as convenções do Armazenamento do Microsoft Azure para garantir solicitações e respostas coerentes em um ambiente que permite uma combinação de leitores e gravadores paralelos. Por exemplo, suporta rastreamento ETag. Para obter mais informações, confira ETags.

Contexto de armazenamento

O contexto para os tipos de armazenamento de blob e contêiner é StorageClientProviderContext, que se parece com:

    string  ClientRequestID { get; }
    JObject ClientRequestIdAsJObject { get; }
    bool    IsMuted { get; set; }
    string  ETag { get; set; }
    bool    TrackingETag { get; set; }

As duas primeiras propriedades são representações diferentes do contexto da operação que foi usado para inicializar a instância StorageClientProviderContext . A classe tem vários construtores, incluindo um construtor de cópia. Outros métodos incluem ResetTo, para permitir a duplicação de estado in-loco e um método estático CreateSafe para garantir que as inicializações problemáticas não gerem exceções.

A classe também contém tratamento especial para criar contextos com base em GUIDs e cadeias de caracteres vazias. Os manipuladores de Notificação de Armazenamento do Azure para blob Criado e Excluído, que também processam notificações decorrentes de agentes externos, exigem o formulário GUID.

Silenciamento de contexto

A propriedade IsMuted controla se o aplicativo espera ou não que o serviço publique notificações resultantes de volta para o chamador, por exemplo, para o sistema externo. Em uma operação silenciada, o serviço não publica eventos resultantes.

Um exemplo são cópias de blob que um codificador executa para organizar blobs no Armazenamento do Microsoft Azure como entrada para uma tarefa de codificação. O sistema externo não está preocupado com esses detalhes, apenas com o status do trabalho de codificação e onde ele pode recuperar as saídas codificadas. Para refletir essas preocupações, o codificador:

  1. Cria um contexto de armazenamento não silenciado com base no contexto de operação de solicitação, por exemplo, ctxNotMuted.

  2. Cria um contexto de armazenamento silenciado, por exemploctxMuted, usando o construtor de cópia de classe de contexto ou criando uma nova instância. Qualquer uma das opções terá o mesmo valor de contexto de operação.

  3. Especifica ctxMuted para as operações de armazenamento envolvidas na configuração para codificação. O sistema externo não vê nenhuma indicação dessas operações ocorrendo.

  4. Especifica o contexto ctxNotMuted para operações de armazenamento que refletem a conclusão da codificação, por exemplo, copiar um arquivo de saída para um contêiner de destino. Os manipuladores do Gridwich publicam os eventos de notificação do Armazenamento do Microsoft Azure resultantes no sistema externo.

O chamador controla a visibilidade final das operações. As operações silenciadas e não silenciadas são baseadas em um valor operationContext equivalente. A intenção do silenciamento de contexto é facilitar a execução do diagnóstico de problemas dos logs de rastreamento de eventos, pois é possível ver as operações de armazenamento relacionadas a uma solicitação, independentemente do status de ativação do silenciamento de operações.

O ResponseBaseDTO tem uma propriedade DoNotPublishbooleana , que o envio de eventos usa para ditar a decisão final sobre a publicação. A expedição de eventos, por sua vez, define a propriedade DoNotPublish com base na propriedade IsMuted do contexto.

O serviço transmite a configuração de silenciamento para o Armazenamento do Azure, que define os clientRequestId eventos de notificação de armazenamento apresentados aos dois manipuladores Gridwich, Criado e Excluído. Esses dois manipuladores definem DoNotPublish para refletir o silenciamento solicitado pelo chamador.

ETags para consistência de destino

O Armazenamento do Microsoft Azure usa o cabeçalho HTTP ETag para sequências de solicitação que devem ter consistência de destino. Um exemplo é garantir que um blob não tenha sido alterado entre as operações Recuperar Metadados e Atualizar Metadados.

Para alinhar com o uso padrão de HTTP, esse cabeçalho tem um valor opaco cuja interpretação é que, se o valor do cabeçalho for alterado, o mesmo ocorrerá com o objeto subjacente. Se uma solicitação enviar o próprio valor atual ETag para o objeto e ele não corresponder ao valor atual ETag do Serviço de Armazenamento, a solicitação falhará imediatamente. Se a solicitação não incluir um valor ETag, o Armazenamento do Microsoft Azure ignorará essa verificação e não bloqueará a solicitação.

ETags no Serviço de Armazenamento

Para o Gridwich, o ETag é um detalhe interno entre o Serviço de Armazenamento do Gridwich e o Armazenamento do Microsoft Azure. Nenhum outro código precisa estar ciente do ETag. O Serviço de Armazenamento usa o ETag para sequências como as operações Get Blob Metadata e Delete Blob para processar uma solicitação BlobDelete Event. O uso de ETag garante que a operação Delete Blob tenha como destino exatamente a mesma versão do blob que a operação Get Metadata.

Para usar o ETag no exemplo anterior:

  1. Envie a solicitação Get Metadata com um ETag em branco.
  2. Salve o valor ETag da resposta.
  3. Adicione o valor ETag salvo à solicitação Excluir Blob.

Se os dois valores ETag forem diferentes, a operação de exclusão falhará. A falha implica que alguma outra operação alterou o blob entre as etapas 2 e 3. Repita o processo da etapa 1.

ETag é um parâmetro de construtores e uma propriedade string da classe StorageClientProviderContext. Somente o BlobClientPipelinePolicy específico de Gridwich manipula o ETag valor.

Controlar o uso de ETag

A propriedade TrackingETag controla se o valor ETag será ou não enviado na próxima solicitação. O valor true significa que o serviço envia um ETag se um estiver disponível.

Uma solicitação do Armazenamento do Microsoft Azure com um valor ETag que não corresponde ao contêiner ou blob da entidade resulta em falha da operação. Essa falha é por design, porque ETag é a maneira HTTP padrão de expressar "a versão exata que a solicitação está direcionando". As solicitações podem incluir a propriedade TrackingETag para indicar que o ETags precisa corresponder ou não incluir a propriedade TrackingETag para indicar que os valores ETag não importam.

O pipeline sempre recupera um valor ETag de uma operação do Armazenamento do Microsoft Azure se há um presente nessa resposta REST. O pipeline sempre atualiza a propriedade de contexto ETag, se possível, desde a última operação. O sinalizador TrackingETag controla apenas se a próxima solicitação da mesma instância do cliente envia o valor da propriedade ETag. Se o valor ETag for nulo ou vazio, a solicitação atual não definirá nenhum valor HTTP ETag, independentemente do valor de TrackingETag.

Sleeves de armazenamento

O Gridwich exige que os próprios mecanismos de armazenamento funcionem para blobs de blocos e contêineres do Armazenamento do Microsoft Azure. Há classes distintas e operações do Serviço de Armazenamento para blobs e contêineres, então não há ambiguidade sobre se uma determinada operação de armazenamento está relacionada a um blob ou a um contêiner.

Um par de classes de provedor, uma para blobs e outra para contêineres, dispensam os dois conjuntos de funcionalidade em unidades chamadas mangas. Os sleeves contêm instâncias de classes auxiliares de armazenamento que fazem parte do SDK do Azure. Inicializar o Serviço de Armazenamento cria os provedores e os disponibiliza diretamente para os métodos do Serviço de Armazenamento.

Estrutura do sleeve

O sleeve é um contêiner para a instância de objeto cliente do SDK e um contexto de armazenamento. As funções do provedor de armazenamento fazem referência ao sleeve por meio das duas propriedades Client e Context. Há um tipo de manga para blobs e outro para contêineres, que têm Client propriedades de tipo BlobBaseCliente BlobContainerClient, respectivamente.

A estrutura geral do sleeve para blobs se parece com:

    BlobBaseClient Client { get; }
    BlobServiceClient Service { get; }
    StorageClientProviderContext Context { get; }

A propriedade Service no sleeve é uma conveniência. Algumas das operações finais relacionadas ao codificador que usam a classe SDK BlobServiceClient exigem chaves de Conta de Armazenamento. Esse requisito levou à adição de uma instância de cliente de serviço aos dois tipos de sleeve existentes, em vez de produzir um provedor separado.

Uso de sleeves

Os provedores de armazenamento do cliente dispensam instâncias de sleeve. O código do serviço de armazenamento é semelhante à seguinte sequência de código anotada, com tipos soletrados para maior clareza:

    public bool DeleteBlob(Uri sourceUri, StorageClientProviderContext context)
    {
        . . .
        StorageBlobClientSleeve sleeve = _blobBaseClientProvider.GetBlobBaseClientForUri(sourceUri, context); // Line A
        BlobProperties propsIncludingMetadata = sleeve.Client.GetProperties(); // Line B
        sleeve.Context.TrackingETag = true;   // Send ETag from GetProperties()
        var wasDeleted = sleeve.Client.DeleteBlob(); // Line C
        sleeve.Context.TrackingETag = false;
        var someResult = sleeve.Client.AnotherOperation(); // Line D
        . . .
    }
  1. O Gridwich preenche automaticamente o contexto de operação no contexto do sleeve na linha A. TrackingETag é false por padrão.
  2. Após a Linha B, sleeve.Context contém a ETag da linha A e mantém o mesmo valor ClientRequestID.
  3. A linha C envia o valor ETag da linha B e do ClientRequestId.
  4. Após a Linha C, o contexto tem um novo valor ETag, conforme retornado na resposta Delete().
  5. A linha D não envia um valor ETag na solicitação para AnotherOperation().
  6. Após a Linha D, o contexto tem um novo valor ETag, conforme retornado na resposta AnotherOperation().

O Serviço de Armazenamento está atualmente definido como Transient na configuração de injeção de dependência, o que implica que o cache baseado em manga é por solicitação. Confira Serviço de Armazenamento e injeção de dependência para obter mais informações.

Alternativas para o Serviço de Armazenamento

As seções a seguir descrevem abordagens alternativas que não fazem parte da solução de armazenamento atual do Gridwich.

Classe AzureStorageManagement do Gridwich

Em conjunto com o membro da mangaService, que é uma instância da classe BlobServiceClient do SDK do Azure, Gridwich também tem a classe AzureStorageManagement. O método GetConnectionStringForAccount do Serviço de Armazenamento e o método GetStoreByNameAsync de codificação Telerek usam essa classe para obter chaves de conta de armazenamento. Atualmente, a classe é baseada na estrutura Fluent. As adições à classe SDK BlobServiceClient devem eventualmente substituir essa classe, permitindo uma recuperação de informações mais focada do que a ampla variedade na interface do Fluent IAzure.

Ocultar a política de pipeline por meio de subclasses

A subclasse dos tipos de cliente do SDK adiciona duas propriedades simples ao cliente, uma para cada valor de cabeçalho HTTP, para ocultar completamente a interação com a política de pipeline. Porém, devido a um bug profundo do Moq, não é possível criar testes de unidade por meio de mock para esses tipos derivados. O Gridwich usa o Moq, portanto, não usou essa abordagem de subclasse.

O bug do Moq refere-se à manipulação incorreta de subclasses de assembly cruzado na presença de funções virtuais de escopo interno. As classes de cliente do SDK usam funções virtuais de escopo interno que envolvem tipos de escopo interno que são invisíveis para usuários externos normais. Quando o Moq tenta criar um mock da subclasse, que está em um dos assemblies do Gridwich, ele falha no tempo de execução do teste, pois não consegue encontrar os virtuais de escopo interno nas classes de cliente do SDK das quais as classes do Gridwich são derivadas. Não há solução alternativa sem alterações na geração de proxy do Moq Castle.

Serviço de Armazenamento e injeção de dependência

O Gridwich atualmente registra o Serviço de Armazenamento como um serviço de injeção de dependência Transient. Ou seja, cada vez que a injeção de dependência é solicitada para o serviço, ela cria uma instância. O código atual também deve funcionar corretamente se o registro for alterado para Scoped, implicando uma instância por solicitação, por exemplo, a solicitação do sistema externo.

No entanto, haverá problemas se o registro for alterado para Singleton, uma instância no aplicativo de funções do Gridwich. O mecanismo de cache do Gridwich para sleeves e intervalos de bytes de dados não diferenciará entre solicitações diferentes. Além disso, o modelo de cache não de check-out, portanto, o Gridwich não remove a instância do cache enquanto ela está em uso. Como as classes de cliente SDK não têm garantia de serem thread-safe, a coordenação exigiria muitas alterações.

Por esses motivos, não altere o Serviço de Armazenamento do Gridwich, como está, para o registro de injeção de dependência Singleton. O Gridwich segue essa regra no registro de injeção de dependência e inclui um teste de unidade, CheckThatStorageServiceIsNotASingleton, para impô-la.

Próximas etapas

Documentação do produto:

Módulos do Microsoft Learn: