fix: 缓存改为 static 解决跨请求并发问题
根因:
- MarketDataService 是 Scoped,每个 HTTP 请求有独立实例
- 两个请求同时查询同一股票,各自有自己的 _memoryCache 和 _pendingPriceRequests
- 导致重复查询数据库,连接冲突
修复:
1. _memoryCache 改为 static,跨请求共享
2. _pendingPriceRequests 改为 static,跨请求共享
3. GetOrAdd 正确模式:先创建 Lazy,再 GetOrAdd
流程:
请求1 → static _memoryCache 未命中 → static _pendingPriceRequests.GetOrAdd
请求2 → static _memoryCache 未命中 → static _pendingPriceRequests.GetOrAdd(复用请求1的 Lazy)
↓
只查一次数据库
This commit is contained in:
parent
5b546061c0
commit
ad7761810d
@ -22,11 +22,11 @@ public class MarketDataService : IMarketDataService
|
||||
private readonly IOkxMarketService _okxService;
|
||||
private readonly IMarketDataRepository _marketDataRepo;
|
||||
|
||||
// 内存缓存层(避免并发数据库查询导致连接池冲突)
|
||||
private readonly ConcurrentDictionary<string, (MarketPriceCache cache, DateTime expireAt)> _memoryCache = new();
|
||||
// 静态内存缓存层(跨请求共享,避免并发数据库查询导致连接池冲突)
|
||||
private static readonly ConcurrentDictionary<string, (MarketPriceCache cache, DateTime expireAt)> _memoryCache = new();
|
||||
|
||||
// 防止并发请求同一股票(使用 Lazy 确保只创建一次)
|
||||
private readonly ConcurrentDictionary<string, Lazy<Task<MarketPriceResponse>>> _pendingPriceRequests = new();
|
||||
// 静态 pending 请求字典(跨请求共享,防止并发请求同一股票)
|
||||
private static readonly ConcurrentDictionary<string, Lazy<Task<MarketPriceResponse>>> _pendingPriceRequests = new();
|
||||
|
||||
public MarketDataService(
|
||||
ILogger<MarketDataService> logger,
|
||||
@ -46,7 +46,7 @@ public class MarketDataService : IMarketDataService
|
||||
|
||||
/// <summary>
|
||||
/// 获取实时价格(自动根据资产类型路由到对应数据源)
|
||||
/// 使用 Lazy 确保同一时间只有一个请求在处理
|
||||
/// 使用静态缓存 + Lazy 确保跨请求并发安全
|
||||
/// </summary>
|
||||
public async Task<MarketPriceResponse> GetPriceAsync(string symbol, string assetType)
|
||||
{
|
||||
@ -55,7 +55,7 @@ public class MarketDataService : IMarketDataService
|
||||
|
||||
try
|
||||
{
|
||||
// 第一步:查内存缓存(快速返回)
|
||||
// 第一步:查静态内存缓存(快速返回)
|
||||
if (_memoryCache.TryGetValue(cacheKey, out var memoryCached) && memoryCached.expireAt > DateTime.Now)
|
||||
{
|
||||
_logger.LogInformation("[内存缓存命中] Symbol={Symbol}, Price={Price}", symbol, memoryCached.cache.Price);
|
||||
@ -69,13 +69,14 @@ public class MarketDataService : IMarketDataService
|
||||
};
|
||||
}
|
||||
|
||||
// 第二步:使用 Lazy 确保同一时间只有一个请求在处理(包括查数据库和获取价格)
|
||||
var lazyTask = _pendingPriceRequests.GetOrAdd(cacheKey,
|
||||
_ => new Lazy<Task<MarketPriceResponse>>(() => GetPriceInternalAsync(symbol, assetType, cacheKey)));
|
||||
// 第二步:创建 Lazy 并尝试添加到字典
|
||||
// 先创建 Lazy,再 GetOrAdd,确保所有线程使用同一个 Lazy
|
||||
var lazy = new Lazy<Task<MarketPriceResponse>>(() => GetPriceInternalAsync(symbol, assetType, cacheKey));
|
||||
var actualLazy = _pendingPriceRequests.GetOrAdd(cacheKey, lazy);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await lazyTask.Value;
|
||||
var result = await actualLazy.Value;
|
||||
_logger.LogInformation("[价格获取成功] Symbol={Symbol}, Price={Price}", symbol, result.Price);
|
||||
return result;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user