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

加入信号量限制请求数量

相关推荐
odoo中国33 分钟前
Odoo 19 模块结构概述
开发语言·python·module·odoo·核心组件·py文件按
代码N年归来仍是新手村成员1 小时前
【Java转Go】即时通信系统代码分析(一)基础Server 构建
java·开发语言·golang
Z1Jxxx2 小时前
01序列01序列
开发语言·c++·算法
沐知全栈开发2 小时前
C语言中的强制类型转换
开发语言
至善迎风2 小时前
Redis完全指南:从诞生到实战
数据库·redis·缓存
关于不上作者榜就原神启动那件事2 小时前
Java中大量数据Excel导入导出的实现方案
java·开发语言·excel
坚定学代码2 小时前
基于观察者模式的ISO C++信号槽实现
开发语言·c++·观察者模式·ai
Wang's Blog2 小时前
Nodejs-HardCore: Buffer操作、Base64编码与zlib压缩实战
开发语言·nodejs
csbysj20203 小时前
C# 集合(Collection)
开发语言
csbysj20203 小时前
Lua 面向对象编程
开发语言