在使用 Azure 地理冗余 (RA-GRS) table 存储时,如何更新 ASP.NET Core 中的 TableServiceClient 以指向次要区域?

How do you update TableServiceClient in ASP.NET Core, to point to a secondary region, when using Azure geo redundant (RA-GRS) table storage?

我正在使用最新的 Azure.Data.Tables nuget 包,版本 12.3.0 连接到 ASP.NET 核心 C# 应用程序中的 Azure table 存储。

如果主要区域发生故障,我的应用程序需要故障转移到次要区域 reads

目前 TableServiceClient 的设置是在 Startup.cs 中完成的,如下所示:

public void ConfigureServices(IServiceCollection services)
{     
   services.AddSingleton(new TableServiceClient(new Uri("PrimaryRegionConnectionURL"), new DefaultAzureCredential()));
}

如何使用指向次要区域的实例更新 TableServiceClient 的当前实例?有没有更好的方法来实现这种故障转移?

澄清一下: 我知道客户不支持故障转移,团队已经创建了一张票以在未来查看此功能。 我意识到我需要一个 TableServiceClient.

的新实例

我只是不确定如何在失败时用指向辅助实例的新实例替换启动时创建的实例

这是使用 TableServiceClient

的代码
    public class TableRepository : ITableStorageRepository
{
    readonly TableServiceClient _serviceClient;

    public TableRepository(TableServiceClient serviceClient)
    {
        _serviceClient = serviceClient;
    }

    public async Task<ICollection<T>> GetPartitionEntities<T>(string partitionKey, string tableName)
        where T : class, ITableEntity, new()
    {
        var listOfEntities = new List<T>();

        var tableClient = _serviceClient.GetTableClient(tableName);

        var queryResults = tableClient.QueryAsync<T>(filter => filter.PartitionKey == partitionKey);

        await foreach (var row in queryResults) 
        {
            listOfEntities.Add(row);
        }

        return listOfEntities;
    }
}

目前不支持使用同一客户端进行自动故障转移;要使用次要区域,需要一个单独的 TableServiceClient。此处提供更多上下文:https://github.com/Azure/azure-sdk-for-net/issues/25456

正在此处跟踪添加支持的工作:https://github.com/Azure/azure-sdk-for-net/issues/25710

不确定这是否是完成它的最佳方式,但考虑到我必须自己处理主要和次要端点之间切换的逻辑,我会这样做。

首先,我会创建两个 TableServiceClient 实例 - 一个用于主要实例,另一个用于辅助实例。

public void ConfigureServices(IServiceCollection services)
{  
    Dictionary<string, TableServiceClient> tableServiceClients = new Dictionary()
    {
      "Primary",  new TableServiceClient(new Uri("PrimaryRegionConnectionURL"), new DefaultAzureCredential()),
      "Secondary",  new TableServiceClient(new Uri("SecondaryRegionConnectionURL"), new DefaultAzureCredential())
    }
    services.AddSingleton(tableServiceClients);
}

接下来,我会在一个单独的函数中提取用于获取实体的逻辑,并将客户端传递给该函数(我们称之为 GetPartitionEntitiesImpl)。

然后在 GetPartitionEntities 方法中,我将尝试从主要端点获取实体并捕获异常。如果异常指示主要端点失败,我会再次调用 GetPartitionEntitiesImpl 函数并尝试从次要端点获取实体。

public class TableRepository : ITableStorageRepository
{
    readonly TableServiceClient _primaryServiceClient, _secondaryServiceClient;

    public TableRepository(Dictionary<string, TableServiceClient> tableServiceClients)
    {
        _primaryServiceClient = tableServiceClients["Primary"];
        _secondaryServiceClient = tableServiceClients["Secondary"];
    }

    public async Task<ICollection<T>> GetPartitionEntities<T>(string partitionKey, string tableName)
        where T : class, ITableEntity, new()
    {
        try
        {
            return await GetPartitionEntitiesImpl(_primaryServiceClient, partitionKey, tableName);
        }
        catch (Exception exception)
        {
          //Check if there is a need for failover
          if (shouldTrySecondaryEndpoint)
          {
              return await GetPartitionEntitiesImpl(_secondaryServiceClient, partitionKey, tableName);
          }
        }
    }

    private async Task<ICollection<T>> GetPartitionEntitiesImpl<T>(TableServiceClient serviceClient, string partitionKey, string tableName)
        where T : class, ITableEntity, new()
    {
        var listOfEntities = new List<T>();

        var tableClient = serviceClient.GetTableClient(tableName);

        var queryResults = tableClient.QueryAsync<T>(filter => filter.PartitionKey == partitionKey);

        await foreach (var row in queryResults) 
        {
            listOfEntities.Add(row);
        }

        return listOfEntities;
    }

}

此外,请查看旧版 Azure Storage SDK(版本 9.x)中有关在主端点和次要端点之间切换的逻辑的代码。该 SDK 可以很好地处理这种情况。