C# --- 本地缓存失效形成缓存击穿触发限流

C# --- 本地缓存失效形成缓存击穿触发限流

问题描述

  • 某一接口前端会偶发返回400.
  • 通过检查日志发现是后端服务调用外部Api时,因为缓存失效导致并发请求数量过多 (QPS接近300), 外部Api返回429导致的.
  • 代码大致如下
csharp 复制代码
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();
	//如果elements数量过多,这里会并发发送大量请求
 	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
  	}
	
	//send reuqets to external Api
	var reponse = await SendTimeZoneRequestAsync(element)
	//parse http response
    var result = ParseHttpResponse(response)
    //set cache
	_timeZoneCache.Add(element.QueryParameter, result)
 }
  • 当缓存失效时,如果elements的数量过多(比如300个),那么上面的代码会并发发送300个请求. 导致触发外部服务的限流,返回429

解决方案

缓存Request Task

  • 经过调查发现,被发送出去的大量请求其实很多是重复的请求
  • 那么可以利用C#的Task机制,加入一个新的缓存,这个缓存用来缓存发送出去的Request Task,避免重复发送请求
csharp 复制代码
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();
	//如果elements数量过多,这里会并发发送大量请求
 	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);
   
   //parse http response
    var result = ParseHttpResponse(response)
   //set cache
	_timeZoneCache.Add(element.QueryParameter, result)
 }
 
 private async Task<HttpResponse> GetTimeZoneRequestTaskAsync(Element element)
 {
  	if (_timeZoneRequestCache.TryGetValue(element.QueryParameter, out string timeZoneRequest)
  	{
  		return timeZoneRequest
  	}

   //do not await here
   var request = SendTimeZoneRequestAsync(element)
   
   //cache the request
   _timeZoneRequestCache(element.QueryParameter, request);
   return request;
 }
 

加入信号量限制请求数量

相关推荐
怪兽20142 小时前
Redis过期键的删除策略有哪些?
java·数据库·redis·缓存·面试
筏.k2 小时前
C++ 设计模式系列:生产者-消费者模式完全指南
开发语言·c++·设计模式
liliangcsdn3 小时前
python如何写数据到excel示例
开发语言·python·excel
workflower5 小时前
单元测试-例子
java·开发语言·算法·django·个人开发·结对编程
YuanlongWang5 小时前
C# 基础——装箱和拆箱
java·开发语言·c#
b78gb6 小时前
电商秒杀系统设计 Java+MySQL实现高并发库存管理与订单处理
java·开发语言·mysql
LXS_3577 小时前
Day 05 C++ 入门 之 指针
开发语言·c++·笔记·学习方法·改行学it
etsuyou8 小时前
js前端this指向规则
开发语言·前端·javascript
shizhenshide9 小时前
为什么有时候 reCAPTCHA 通过率偏低,常见原因有哪些
开发语言·php·验证码·captcha·recaptcha·ezcaptcha
mit6.8249 小时前
[Agent可视化] 配置系统 | 实现AI模型切换 | 热重载机制 | fsnotify库(go)
开发语言·人工智能·golang