From ad7761810d24ba6fa59c76da3df059aec0e1a335 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Tue, 24 Mar 2026 10:30:55 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BC=93=E5=AD=98=E6=94=B9=E4=B8=BA=20s?= =?UTF-8?q?tatic=20=E8=A7=A3=E5=86=B3=E8=B7=A8=E8=AF=B7=E6=B1=82=E5=B9=B6?= =?UTF-8?q?=E5=8F=91=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因: - 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) ↓ 只查一次数据库 --- .../Services/MarketDataService.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/AssetManager.Infrastructure/Services/MarketDataService.cs b/AssetManager.Infrastructure/Services/MarketDataService.cs index 41e02fe..5231d0c 100755 --- a/AssetManager.Infrastructure/Services/MarketDataService.cs +++ b/AssetManager.Infrastructure/Services/MarketDataService.cs @@ -22,11 +22,11 @@ public class MarketDataService : IMarketDataService private readonly IOkxMarketService _okxService; private readonly IMarketDataRepository _marketDataRepo; - // 内存缓存层(避免并发数据库查询导致连接池冲突) - private readonly ConcurrentDictionary _memoryCache = new(); + // 静态内存缓存层(跨请求共享,避免并发数据库查询导致连接池冲突) + private static readonly ConcurrentDictionary _memoryCache = new(); - // 防止并发请求同一股票(使用 Lazy 确保只创建一次) - private readonly ConcurrentDictionary>> _pendingPriceRequests = new(); + // 静态 pending 请求字典(跨请求共享,防止并发请求同一股票) + private static readonly ConcurrentDictionary>> _pendingPriceRequests = new(); public MarketDataService( ILogger logger, @@ -46,7 +46,7 @@ public class MarketDataService : IMarketDataService /// /// 获取实时价格(自动根据资产类型路由到对应数据源) - /// 使用 Lazy 确保同一时间只有一个请求在处理 + /// 使用静态缓存 + Lazy 确保跨请求并发安全 /// public async Task 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>(() => GetPriceInternalAsync(symbol, assetType, cacheKey))); + // 第二步:创建 Lazy 并尝试添加到字典 + // 先创建 Lazy,再 GetOrAdd,确保所有线程使用同一个 Lazy + var lazy = new Lazy>(() => 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; }