像AspNetCoreRateLimit这种轮子我前面有给大家介绍过,今天就不说了,我们来聊聊背后的原理,欢迎各位大佬指正!
像我們經常看的一些 api 請求接口網站:

拿請求國外主要城市的七日接口舉例,非 vip 只能使用 2000 次, vip 用戶一天最多請求 10000 次,請求該接口時,必須要註冊帳號獲取到 appid 和密鑰。
那我們根據這個需求,設計一個獲取天氣的限流接口。
第一步
校驗登錄帳號是否存在,如果不存在,我們拋出不存在的錯誤
[HttpPost("GetWeather")]
public IActionResult ApiLimit(WeatherInfor weatherInfor)
{
if (!_userService.IsValid(weatherInfor.Appid, weatherInfor.Appsecret))
{
throw new Exception("账号或者密码错误");
}
}
第二步
判斷該帳戶是否是 vip 用戶,如果是 vip 用戶,則沒有調用總次數,只有單日限制次數,像這種單日限制次數,隔天清空的數據,我們肯定用緩存來處理比較合理,我們設置每天的 23 點 59 分 59 秒所有請求緩存是否有效,這就是緩存的絕對過期時間。
那具體業務邏輯就是這樣的,由於用戶的 appid 是唯一的,我們可以把它當作 key 值,調用的次數當作 value 值,如果緩存不存在我們就添加緩存,如果緩存存在 我們就獲取調用次數,如果大於 2000 我們就告訴調用方,調用次數已用完,如果沒有 我們就從緩存中獲取調用的次數,並給它+1。
緩存類
public class MemoryCacheHelper
{
public static MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
/// <summary>
/// 验证缓存项是否存在
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public static bool Exists(string key)
{
if (key == null)
{
return false;
}
return _cache.TryGetValue(key, out _);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public static object Get(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (!Exists(key))
throw new ArgumentNullException(nameof(key));
return _cache.Get(key);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
public static bool AddMemoryCache(string key, object value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
DateTime time = Convert.ToDateTime(DateTime.Now.AddDays(1).ToString("D").ToString()).AddSeconds(-1);
_cache.Set(key, value, time);
return Exists(key);
}
}
業務邏輯
public bool IsLimit(string appId)
{
if (MemoryCacheHelper.Exists(appId))
{
int value = int.Parse(MemoryCacheHelper.Get(appId).ToString());
if (value > 2000)
return false;
else
value = value + 1;
MemoryCacheHelper.AddMemoryCache(appId, value);
}
else
{
MemoryCacheHelper.AddMemoryCache(appId, 1);
}
return true;
}
最後
去查詢天氣接口, 返回數據
[HttpPost("GetWeather")]
public IActionResult ApiLimit(WeatherInfor weatherInfor)
{
if (!_userService.IsValid(weatherInfor.Appid, weatherInfor.Appsecret))
{
throw new Exception("账号或者密码错误");
}
bool isLimit = false;
if (_userService.IsVIP(weatherInfor.Appid))
{
isLimit= _sqlServices.IsLimit(weatherInfor.Appid);
}
else
{
isLimit = _memoryCacheServices.IsLimit(weatherInfor.Appid);
}
if (isLimit)
{
//查询天气接口 返回数据
}
else
{
throw new Exception("调用次数已用完");
}
return Ok("");
}
最後大家如果喜歡我的文章,還麻煩給個關注並點個讚, 希望 net 生態圈越來越好!