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.AzureStorage
participante 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 BlobBaseClient e BlobContainerClient do SDK 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.
No 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".
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 do 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
Praticamente todas as operações do Serviço de Armazenamento do Gridwich requerem 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, é compatível com o rastreamento de ETag. Para obter mais informações, confira ETags.
Contexto de armazenamento
O contexto dos tipos de armazenamento é StorageClientProviderContext, que tem a seguinte aparência:
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 de 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 da Notificação de Armazenamento do Azure para o Created e Deleted, do blob, que também processam as notificações provenientes de agentes externos, requerem o formato 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:
Cria um contexto de armazenamento não silenciado com base no contexto de operação de solicitação, por exemplo,
ctxNotMuted
.Cria um contexto de armazenamento silenciado, por exemplo,
ctxMuted
, usando o construtor de cópia de classe de contexto ou criando uma instância. Qualquer uma das opções terá o mesmo valor de contexto de operação.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.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 booliana DoNotPublish
, que a expedição 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 depois define o clientRequestId
nos eventos de notificação de armazenamento que ele apresenta aos dois manipuladores do Gridwich Created e Deleted. 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:
- Envie a solicitação Get Metadata com um
ETag
em branco. - Salve o valor
ETag
da resposta. - 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 de cadeia de caracteres de StorageClientProviderContext class. Somente a BlobClientPipelinePolicy específica ao Gridwich manipula o valor ETag
.
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 uma para containers, dispensam os dois conjuntos de funcionalidade em unidades chamadas sleeves. 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 sleeve para blobs e outro para contêineres, que têm Client
propriedades de tipo BlobBaseClient
e 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 que requer credenciais 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
. . .
}
- O Gridwich preenche automaticamente o contexto de operação no contexto do sleeve na linha A.
TrackingETag
é false por padrão. - Após a Linha B,
sleeve.Context
contém aETag
da linha A e mantém o mesmo valorClientRequestID
. - A linha C envia o valor
ETag
da linha B e doClientRequestId
. - Após a Linha C, o contexto tem um novo valor
ETag
, conforme retornado na respostaDelete()
. - A linha D não envia um valor
ETag
na solicitação paraAnotherOperation()
. - Após a Linha D, o contexto tem um novo valor
ETag
, conforme retornado na respostaAnotherOperation()
.
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 sleeve está em uma base 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.
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 do SDK não têm a 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:
- Sistema de mídia de nuvem do Gridwich
- O que é o Armazenamento de Blobs do Azure?
- O que é o Azure Pipelines?
Módulos do Microsoft Learn: