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
相关产品推荐
相关产品推荐

