C# --- 本地缓存失效形成缓存击穿触发限流
- 问题描述
- 解决方案
- 缓存Request Task
- 加入信号量限制请求数量
问题描述
- 某一接口前端会偶发返回400.
- 通过检查日志发现是后端服务调用外部Api时,因为缓存失效导致并发请求数量过多 (QPS接近300), 外部Api返回429导致的.
- 代码大致如下
private readonly MemoryCache _timeZoneCache = new(new MemoryCacheOptions())private MemeoryCacheEntryOptions EntryOption => new MemeoryCacheEntryOption().SetAbsoluteExpiraton(DataTimeOffset.Utc.Now.AddMinutes(1))public async Task Process()
{var elements = await GetAllElementsAsync();var tasks = elements.Select(element -> GetTimeZoneAsync(element)).ToList();if (tasks.Any()){await Task.WhenAll(tasks)}}private async Task<string> GetTimeZoneAsync(Element element){if (_timeZoneCache.TryGetValue(element.QueryParameter, out string timeZone){return timeZone}var reponse = await SendTimeZoneRequestAsync(element)var result = ParseHttpResponse(response)_timeZoneCache.Add(element.QueryParameter, result)}
- 当缓存失效时,如果elements的数量过多(比如300个),那么上面的代码会并发发送300个请求. 导致触发外部服务的限流,返回429
解决方案
缓存Request Task
- 经过调查发现,被发送出去的大量请求其实很多是重复的请求
- 那么可以利用C#的Task机制,加入一个新的缓存,这个缓存用来缓存发送出去的Request Task,避免重复发送请求
private readonly MemoryCache _timeZoneCache = new(new MemoryCacheOptions())private MemeoryCacheEntryOptions EntryOption => new MemeoryCacheEntryOption().SetAbsoluteExpiraton(DataTimeOffset.Utc.Now.AddMinutes(1))public async Task Process()
{var elements = await GetAllElementsAsync();var tasks = elements.Select(element -> GetTimeZoneAsync(element)).ToList();if (tasks.Any()){await Task.WhenAll(tasks)}}private async Task<string> GetTimeZoneAsync(Element element){if (_timeZoneCache.TryGetValue(element.QueryParameter, out string timeZone){return timeZone}var response = await GetTimeZoneRequestTaskAsync(element);var result = ParseHttpResponse(response)_timeZoneCache.Add(element.QueryParameter, result)}private async Task<HttpResponse> GetTimeZoneRequestTaskAsync(Element element){if (_timeZoneRequestCache.TryGetValue(element.QueryParameter, out string timeZoneRequest){return timeZoneRequest}var request = SendTimeZoneRequestAsync(element)_timeZoneRequestCache(element.QueryParameter, request);return request;}
加入信号量限制请求数量
- 如果都是不同的请求,那么可以使用信号量控制并发发送请求的数量
参见 https://blog.csdn.net/weixin_38803409/article/details/135353000