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;
 }
 

加入信号量限制请求数量

相关推荐
会博通·代码搬运工3 分钟前
会博通API对接实战:工程企业文档分布式采集系统的技术实现与Python SDK详解
开发语言·分布式·python·线性代数·矩阵·架构·电子档案合规
林森lsjs23 分钟前
完结撒花!Java SE 语法阶段总结!
java·开发语言
lupai23 分钟前
短信接口快速接入与调用实战指南
java·开发语言·数据库
刘名喜30 分钟前
第12篇-Gradle-Kotlin-DSL构建指南
开发语言·kotlin·springboot
大鱼>42 分钟前
DSPy:LLM程序自动编译与提示词优化
开发语言·人工智能·python·深度学习
撩妹帝九歌1 小时前
Java文件写入与编码、字节数组、字符集、字符编解码 一文打通!
java·开发语言
星核0penstarry1 小时前
Solon AI v4.0.4 技术分析:Java Agent 框架的兼容性突破与选型考量 基于 OSCHINA 2026-07-30 报道及公开技术资料整理
java·开发语言·人工智能
昨夜星河入梦来1 小时前
postman接口测试报错503的解决方法
开发语言·postman
spider_xcxc2 小时前
Jenkins 遇见 Argo CD:构建可靠 GitOps 流水线的实战指南
java·开发语言·servlet
CryptoPP2 小时前
韩国股市数据源对接实战:从KOSPI到KOSDAQ的实时行情接入方案
数据库·redis·缓存·金融·区块链