fix: 增强价格获取稳定性和日志追踪
问题排查: 1. 缓存命中时验证价格有效性(Price > 0) 2. 外部 API 返回无效价格时拒绝写入缓存 3. 缓存查询层添加详细日志 4. 捕获缓存查询异常并返回 null 改进: - 缓存价格 <= 0 时忽略缓存重新获取 - 外部 API 价格 <= 0 时抛出异常,避免污染缓存 - 详细日志追踪价格获取全流程
This commit is contained in:
parent
3768f6e747
commit
3fb2403e85
@ -25,9 +25,30 @@ public class MarketDataRepository : IMarketDataRepository
|
|||||||
public async Task<MarketPriceCache?> GetPriceCacheAsync(string symbol, string assetType)
|
public async Task<MarketPriceCache?> GetPriceCacheAsync(string symbol, string assetType)
|
||||||
{
|
{
|
||||||
var cacheKey = GenerateCacheKey(symbol, assetType);
|
var cacheKey = GenerateCacheKey(symbol, assetType);
|
||||||
return await _db.Queryable<MarketPriceCache>()
|
_logger.LogDebug("[缓存查询] CacheKey={CacheKey}, Symbol={Symbol}, AssetType={AssetType}", cacheKey, symbol, assetType);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _db.Queryable<MarketPriceCache>()
|
||||||
.Where(p => p.Id == cacheKey && p.ExpiredAt > DateTime.Now)
|
.Where(p => p.Id == cacheKey && p.ExpiredAt > DateTime.Now)
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("[缓存查询成功] CacheKey={CacheKey}, Price={Price}, ExpiredAt={ExpiredAt}", cacheKey, result.Price, result.ExpiredAt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogDebug("[缓存查询无结果] CacheKey={CacheKey}", cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "[缓存查询异常] CacheKey={CacheKey}", cacheKey);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SavePriceCacheAsync(MarketPriceCache cache)
|
public async Task<bool> SavePriceCacheAsync(MarketPriceCache cache)
|
||||||
|
|||||||
@ -48,14 +48,16 @@ public class MarketDataService : IMarketDataService
|
|||||||
public async Task<MarketPriceResponse> GetPriceAsync(string symbol, string assetType)
|
public async Task<MarketPriceResponse> GetPriceAsync(string symbol, string assetType)
|
||||||
{
|
{
|
||||||
var cacheKey = $"{symbol.ToUpper()}_{assetType.ToUpper()}";
|
var cacheKey = $"{symbol.ToUpper()}_{assetType.ToUpper()}";
|
||||||
_logger.LogInformation("获取实时价格: {Symbol}, 资产类型: {AssetType}", symbol, assetType);
|
_logger.LogInformation("[价格查询开始] Symbol={Symbol}, AssetType={AssetType}, CacheKey={CacheKey}", symbol, assetType, cacheKey);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
// 先查缓存
|
// 先查缓存
|
||||||
var cached = await _marketDataRepo.GetPriceCacheAsync(symbol, assetType);
|
var cached = await _marketDataRepo.GetPriceCacheAsync(symbol, assetType);
|
||||||
|
|
||||||
if (cached != null)
|
if (cached != null && cached.Price > 0) // ← 验证价格有效
|
||||||
{
|
{
|
||||||
_logger.LogDebug("缓存命中: {Symbol} {AssetType}, 价格: {Price}", symbol, assetType, cached.Price);
|
_logger.LogInformation("[缓存命中] Symbol={Symbol}, Price={Price}, ExpiredAt={ExpiredAt}", symbol, cached.Price, cached.ExpiredAt);
|
||||||
return new MarketPriceResponse
|
return new MarketPriceResponse
|
||||||
{
|
{
|
||||||
Symbol = cached.Symbol,
|
Symbol = cached.Symbol,
|
||||||
@ -66,12 +68,23 @@ public class MarketDataService : IMarketDataService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cached != null && cached.Price <= 0)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("[缓存命中但价格无效] Symbol={Symbol}, Price={Price},忽略缓存重新获取", symbol, cached.Price);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("[缓存未命中] Symbol={Symbol}, AssetType={AssetType},需要从数据源获取", symbol, assetType);
|
||||||
|
}
|
||||||
|
|
||||||
// 使用 GetOrAdd 模式防止并发重复请求
|
// 使用 GetOrAdd 模式防止并发重复请求
|
||||||
var priceTask = _pendingPriceRequests.GetOrAdd(cacheKey, _ => FetchPriceFromSourceAsync(symbol, assetType));
|
var priceTask = _pendingPriceRequests.GetOrAdd(cacheKey, _ => FetchPriceFromSourceAsync(symbol, assetType));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await priceTask;
|
var result = await priceTask;
|
||||||
|
_logger.LogInformation("[价格获取成功] Symbol={Symbol}, Price={Price}", symbol, result.Price);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -79,6 +92,12 @@ public class MarketDataService : IMarketDataService
|
|||||||
_pendingPriceRequests.TryRemove(cacheKey, out _);
|
_pendingPriceRequests.TryRemove(cacheKey, out _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "[价格获取异常] Symbol={Symbol}, AssetType={AssetType}", symbol, assetType);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从数据源获取价格(内部方法)
|
/// 从数据源获取价格(内部方法)
|
||||||
@ -120,6 +139,13 @@ public class MarketDataService : IMarketDataService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证价格有效性
|
||||||
|
if (response.Price <= 0)
|
||||||
|
{
|
||||||
|
_logger.LogError("[价格无效] Symbol={Symbol}, Price={Price}, Source={Source},不写入缓存", symbol, response.Price, source);
|
||||||
|
throw new InvalidOperationException($"获取到的价格无效: {symbol} = {response.Price}");
|
||||||
|
}
|
||||||
|
|
||||||
// 写入缓存
|
// 写入缓存
|
||||||
await SavePriceCacheAsync(symbol, assetType, response, source);
|
await SavePriceCacheAsync(symbol, assetType, response, source);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user