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

加入信号量限制请求数量

相关推荐
少控科技5 分钟前
QT第6个程序 - 网页内容摘取
开发语言·qt
darkb1rd6 分钟前
八、PHP SAPI与运行环境差异
开发语言·网络安全·php·webshell
历程里程碑8 分钟前
Linux20 : IO
linux·c语言·开发语言·数据结构·c++·算法
郝学胜-神的一滴10 分钟前
深入浅出:使用Linux系统函数构建高性能TCP服务器
linux·服务器·开发语言·网络·c++·tcp/ip·程序人生
清风拂山岗 明月照大江13 分钟前
Redis笔记汇总
java·redis·缓存
承渊政道14 分钟前
Linux系统学习【Linux系统的进度条实现、版本控制器git和调试器gdb介绍】
linux·开发语言·笔记·git·学习·gitee
JQLvopkk38 分钟前
C# 轻量级工业温湿度监控系统(含数据库与源码)
开发语言·数据库·c#
玄同7651 小时前
从 0 到 1:用 Python 开发 MCP 工具,让 AI 智能体拥有 “超能力”
开发语言·人工智能·python·agent·ai编程·mcp·trae
czy87874751 小时前
深入了解 C++ 中的 `std::bind` 函数
开发语言·c++
消失的旧时光-19431 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed