Segurança de thread e gerenciamento do tempo de vida do cliente para objetos do SDK do Azure
Este artigo ajuda você a entender os problemas de segurança de thread ao usar o SDK do Azure. Ele também discute como o design do SDK afeta o gerenciamento do tempo de vida do cliente. Você aprenderá por que é desnecessário descartar objetos de cliente do SDK do Azure.
Segurança de roscas
Todos os objetos de cliente do SDK do Azure são thread-safe e independentes uns dos outros. Esse design garante que a reutilização de instâncias de cliente seja sempre segura, mesmo entre threads. Por exemplo, o código a seguir inicia várias tarefas, mas é thread safe:
var client = new SecretClient(
new Uri("<secrets_endpoint>"), new DefaultAzureCredential());
foreach (var secretName in secretNames)
{
// Using clients from parallel threads
Task.Run(() => Console.WriteLine(client.GetSecret(secretName).Value));
}
Os objetos de modelo usados pelos clientes SDK, sejam modelos de entrada ou saída, não são thread-safe por padrão. A maioria dos casos de uso envolvendo objetos de modelo usa apenas um único thread. Portanto, o custo de implementar a sincronização como um comportamento padrão é muito alto para esses objetos. O código a seguir ilustra um bug no qual acessar um modelo a partir de vários threads pode causar um comportamento indefinido:
KeyVaultSecret newSecret = client.SetSecret("secret", "value");
foreach (var tag in tags)
{
// Don't use model type from parallel threads
Task.Run(() => newSecret.Properties.Tags[tag] = CalculateTagValue(tag));
}
client.UpdateSecretProperties(newSecret.Properties);
Para acessar o modelo de threads diferentes, você deve implementar seu próprio código de sincronização. Por exemplo:
KeyVaultSecret newSecret = client.SetSecret("secret", "value");
// Code omitted for brevity
foreach (var tag in tags)
{
Task.Run(() =>
{
lock (newSecret)
{
newSecret.Properties.Tags[tag] = CalculateTagValue(tag);
}
);
}
client.UpdateSecretProperties(newSecret.Properties);
Vida útil do cliente
Como os clientes do SDK do Azure são thread-safe, não há razão para construir vários objetos de cliente SDK para um determinado conjunto de parâmetros do construtor. Trate os objetos de cliente do SDK do Azure como singletons depois de construídos. Essa recomendação geralmente é implementada registrando objetos de cliente do SDK do Azure como singletons no contêiner Inversion of Control (IoC) do aplicativo. A injeção de dependência (DI) é usada para obter referências ao objeto do cliente SDK. O exemplo a seguir mostra um registro de objeto de cliente singleton:
var builder = Host.CreateApplicationBuilder(args);
var endpoint = builder.Configuration["SecretsEndpoint"];
var blobServiceClient = new BlobServiceClient(
new Uri(endpoint), new DefaultAzureCredential());
builder.Services.AddSingleton(blobServiceClient);
Para obter mais informações sobre como implementar DI com o SDK do Azure, consulte Injeção de dependência com o SDK do Azure para .NET.
Como alternativa, você pode criar uma instância de cliente SDK e fornecê-la a métodos que exigem um cliente. O objetivo é evitar instanciações desnecessárias do mesmo objeto cliente SDK com os mesmos parâmetros. É desnecessário e desperdiçador.
Os clientes não são descartáveis
Duas perguntas finais que surgem frequentemente são:
- Preciso descartar objetos de cliente do SDK do Azure quando terminar de usá-los?
- Por que os objetos de cliente do SDK do Azure baseados em HTTP não são descartáveis?
Internamente, todos os clientes do SDK do Azure usam uma única instância compartilhada HttpClient
. Os clientes não criam outros recursos que precisem ser ativamente liberados. A instância compartilhada HttpClient
persiste durante todo o tempo de vida do aplicativo.
// Both clients reuse the shared HttpClient and don't need to be disposed
var blobClient = new BlobClient(new Uri(sasUri));
var blobClient2 = new BlobClient(new Uri(sasUri2));
É possível fornecer uma instância personalizada de para um objeto de cliente do SDK do HttpClient
Azure. Neste caso, você se torna responsável por gerenciar a HttpClient
vida útil e descartá-la corretamente no momento certo.
var httpClient = new HttpClient();
var clientOptions = new BlobClientOptions()
{
Transport = new HttpClientTransport(httpClient)
};
// Both clients would use the HttpClient instance provided in clientOptions
var blobClient = new BlobClient(new Uri(sasUri), clientOptions);
var blobClient2 = new BlobClient(new Uri(sasUri2), clientOptions);
// Code omitted for brevity
// You're responsible for properly disposing httpClient some time later
httpClient.Dispose();
Orientações adicionais para o gerenciamento e o descarte adequados de HttpClient
instâncias podem ser encontradas na HttpClient documentação.