From 3768f6e74770043c93731753532c0cca3588f148 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Tue, 24 Mar 2026 08:57:45 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=98=B2=E6=AD=A2=E4=BB=B7=E6=A0=BC?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=B9=B6=E5=8F=91=E7=AB=9E=E6=80=81=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:多个并发请求同时获取同一股票价格,导致: 1. 外部 API 被重复调用(可能被限流) 2. 部分请求失败导致收益率显示 0% 解决方案: - 使用 ConcurrentDictionary 存储进行中的请求 - GetOrAdd 模式确保同一时间只有一个请求在获取价格 - 其他并发请求等待第一个请求的结果 - 请求完成后移除 pending 任务 --- .../Services/MarketDataService.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/AssetManager.Infrastructure/Services/MarketDataService.cs b/AssetManager.Infrastructure/Services/MarketDataService.cs index d5e5206..cb281e0 100755 --- a/AssetManager.Infrastructure/Services/MarketDataService.cs +++ b/AssetManager.Infrastructure/Services/MarketDataService.cs @@ -2,6 +2,7 @@ using AssetManager.Data; using AssetManager.Data.Repositories; using AssetManager.Models.DTOs; using Microsoft.Extensions.Logging; +using System.Collections.Concurrent; namespace AssetManager.Infrastructure.Services; @@ -20,6 +21,9 @@ public class MarketDataService : IMarketDataService private readonly ITiingoMarketService _tiingoService; private readonly IOkxMarketService _okxService; private readonly IMarketDataRepository _marketDataRepo; + + // 防止并发请求同一股票 + private readonly ConcurrentDictionary> _pendingPriceRequests = new(); public MarketDataService( ILogger logger, @@ -39,9 +43,11 @@ public class MarketDataService : IMarketDataService /// /// 获取实时价格(自动根据资产类型路由到对应数据源) + /// 使用并发控制防止重复请求 /// public async Task GetPriceAsync(string symbol, string assetType) { + var cacheKey = $"{symbol.ToUpper()}_{assetType.ToUpper()}"; _logger.LogInformation("获取实时价格: {Symbol}, 资产类型: {AssetType}", symbol, assetType); // 先查缓存 @@ -60,7 +66,27 @@ public class MarketDataService : IMarketDataService }; } - // 缓存未命中,调用API + // 使用 GetOrAdd 模式防止并发重复请求 + var priceTask = _pendingPriceRequests.GetOrAdd(cacheKey, _ => FetchPriceFromSourceAsync(symbol, assetType)); + + try + { + return await priceTask; + } + finally + { + // 请求完成后移除(无论成功失败) + _pendingPriceRequests.TryRemove(cacheKey, out _); + } + } + + /// + /// 从数据源获取价格(内部方法) + /// + private async Task FetchPriceFromSourceAsync(string symbol, string assetType) + { + _logger.LogInformation("从数据源获取价格: {Symbol}, 资产类型: {AssetType}", symbol, assetType); + MarketPriceResponse response; string source;