You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# Web API并发调用AmazonPollyClient性能问题及缓存方案咨询

方案问题分析与优化建议

现有方案存在的问题

  • 线程安全隐患与逻辑错误:
    代码中的client是类的实例成员变量,外层的if (client == null)判断不具备线程安全性。当多请求并发处理时会出现两种问题:一是同配置的并发请求可能重复创建客户端(竞态条件);二是不同配置的请求会错误复用同一个client实例,导致用错Polly客户端的配置信息。
  • 缺乏配置更新处理机制:如果客户更新了Polly配置(比如更换AccessKey、切换Region),缓存中的旧客户端不会被替换,后续请求依然会使用旧配置的客户端,引发业务错误。
  • 客户端生命周期管理缺失:没有考虑闲置客户端的清理,长期运行后可能积累大量不再使用的客户端实例,占用不必要的系统资源。

优化后的实现思路

1. 修正缓存核心逻辑

去掉实例级client变量,直接基于缓存Key获取对应客户端,利用ConcurrentDictionary的GetOrAdd方法简化线程安全的创建逻辑:

private static readonly ConcurrentDictionary<string, AmazonPollyClient> _clientCache = new();

private AmazonPollyClient GetOrCreateClient(int busNo, string configName, string VendorName, PollyTTSConfig pollyTTSConfig)
{
    if (!validateConfig(pollyTTSConfig))
    {
        _logger.LogError($"Invalid config for {busNo}-{configName}-{VendorName}");
        throw new InvalidOperationException("Invalid Polly TTS configuration");
    }

    var cacheKey = CacheKeyForVoiceList(busNo, configName, VendorName);
    return _clientCache.GetOrAdd(cacheKey, key =>
    {
        var regionEndpoint = RegionEndpoint.GetBySystemName(pollyTTSConfig.Region);
        var awsCreds = new BasicAWSCredentials(pollyTTSConfig.AccessKey, pollyTTSConfig.SecretKey);
        var client = new AmazonPollyClient(awsCreds, regionEndpoint);
        logClientDetails(pollyTTSConfig);
        _logger.LogInformation($"Created new Polly client for key: {key}");
        return client;
    });
}

GetOrAdd方法本身是线程安全的,能避免同Key下的重复创建问题,同时直接返回对应Key的客户端,不会出现不同配置复用实例的错误。

2. 增加配置更新支持

当客户更新配置时,提供主动清理缓存的方法,确保下次请求会创建新的客户端:

public void InvalidateClientCache(int busNo, string configName, string VendorName)
{
    var cacheKey = CacheKeyForVoiceList(busNo, configName, VendorName);
    if (_clientCache.TryRemove(cacheKey, out var client))
    {
        client.Dispose(); // 释放旧客户端资源
        _logger.LogInformation($"Invalidated Polly client for key: {cacheKey}");
    }
}

在配置更新的业务逻辑中调用此方法即可完成缓存刷新。

3. 闲置客户端清理(可选)

如果存在大量临时配置或客户不再使用某些配置,可以定期清理闲置的客户端。比如结合ConcurrentDictionary记录客户端最后使用时间:

// 新增缓存项的过期时间记录
private static readonly ConcurrentDictionary<string, DateTime> _clientLastUsedTime = new();

// 在获取客户端时更新最后使用时间
private AmazonPollyClient GetOrCreateClient(...)
{
    var client = _clientCache.GetOrAdd(...);
    _clientLastUsedTime.AddOrUpdate(cacheKey, DateTime.UtcNow, (k, v) => DateTime.UtcNow);
    return client;
}

// 定期清理任务(可通过后台定时任务执行)
private void CleanupIdleClients(TimeSpan idleThreshold)
{
    var now = DateTime.UtcNow;
    var keysToRemove = _clientLastUsedTime.Where(kv => now - kv.Value > idleThreshold)
                                         .Select(kv => kv.Key)
                                         .ToList();
    foreach (var key in keysToRemove)
    {
        if (_clientCache.TryRemove(key, out var client))
        {
            client.Dispose();
            _clientLastUsedTime.TryRemove(key, out _);
            _logger.LogInformation($"Cleaned up idle Polly client for key: {key}");
        }
    }
}

4. 其他性能优化点

  • 预创建热点配置客户端:对于高频使用的客户配置,在系统启动或配置加载时提前创建客户端并放入缓存,避免首次请求的初始化延迟。
  • 复用AWS Credentials:如果多个配置使用相同的Credentials,可以单独缓存AWSCredentials实例,减少重复创建开销。

内容的提问来源于stack exchange,提问作者Ankush Jindal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 21:47:06