请求量限制方法-使用本地Cache记录当前请求量[坑]

  有个需求:需要限制每个账户请求服务器的次数(该次数可以配置在DB,xml文件或其他)。单位:X次/分钟。若1分钟内次数<=X 则允许访问,1分钟内次数>X则不再允许访问。   这类需求很常见的,像请求Baidu Map Api服务接口等,都有这类限制,只是单位不同。

  一般来说,接口的请求在服务器端都会有记录的,比如记在DB中,记录 账户、请求时间、请求信息、请求操作、服务器响应信息 等。所以逻辑上完全可以获取请求当前时间段的请求量(执行记录 的count()操作,在DB中为Sql : SELECT COUNT(*) FROM RequestRecordTable where Account = .. And ReqTime .. )。但这样不现实:为这么一个小功能竟然还要耗时去计算,完全浪费计算资源、内存资源,一旦请求量、记录数大增,那么性能肯定很差。

  于是就想到了缓存:1.把该账户的最大请求量放在缓存里,永不过期。2.把该账户的1分钟以来的请求量放在缓存中,1分钟过期自动释放。  3.每来一次请求,则当前请求量+1,后与该账户的最大请求量比对,<=则允许,>则超过次数。为了简单起见,使用.Net 本地缓存。详见代码(该账户的最大请求量已获取到,若获取失败则读配置:

 1  /// <summary>
 2         /// 验证该账户当前请求量是否超过限制
 3         /// </summary>
 4         /// <param name="account">账户</param>
 5         /// <param name="maxNumLimit">最大请求量,已获取到</param>
 6         /// <returns>true-超过,false-未超过</returns>
 7         public bool VerifyMaxNumLimit(string account, int maxNumLimit)
 8         {
 9             string currentSendNumCacheKey = string.Format("SysCode_{0}_CurrentSendNum", account);
10             object currentSendNumObj = HttpContext.Current.Cache[currentSendNumCacheKey];
11             int currentSendNum = 0;
12             if (currentSendNumObj != null)  //缓存存在
13             {
14                 currentSendNum = (int)currentSendNumObj;
15                 currentSendNum++;
16                 HttpContext.Current.Cache[currentSendNumCacheKey] = currentSendNum;
17             }
18             else   //缓存失效,已到期或者缓存问题。重新写入:目前请求次数 1次,过期时间 1分钟
19             {
20                 currentSendNum = 1;
21                 HttpContext.Current.Cache.Insert(currentSendNumCacheKey, 1,null, DateTime.Now.AddMinutes(1),Cache.NoSlidingExpiration);
22             }
23
24             return currentSendNum > maxNumLimit;
25         }

乍一看,完全没有问题,大功告成。可是,当你本地自己测试时,却发现 :前几次不超过限制量次数的调用接口都是成功的,但是一旦超过该请求量以后,哪怕是5分钟后,也无法在此请求。究其原因:VerifyMaxNumLimit 此后一直返回true=> 1分钟后缓存没有清除,仍然存在。

最终原因是什么??

1.是15行:  HttpContext.Current.Cache[currentSendNumCacheKey] = currentSendNum;

这句代码看似只是修改缓存值。可实际上,这句代码等效于:HttpContext.Current.Cache.Insert(currentSendNumCacheKey, currentSendNum)!即覆盖缓存,并将缓存设置为永不过期(除非IIS应用程序池回收,或内存不足等外部情况)=》缓存不失效=》当前请求量不断++,所以 return currentSendNum > maxNumLimit   一直是true.

2.即使把1解决了,还有一个不易发现的BUG,不容易看出来,是多个线程使用并修改统一资源=》诱发并发问题:当同账号的请求1和请求2同时来临,可能请求1的line 16 与请求2的line21 是先后执行的。就出现了脏读写。

所以,需要解决:1.找到一个可修改缓存值又不修改缓存失效时间的方法(本地缓存HttpContext.Current.Cache 似乎无法实现,放弃,2.加锁或做成单例。

我公司正好有Redis组件,满足上述要去并且本身封装时即为线程安全的。所以最终我用了Redis来记录账户当前请求量。具体代码其实类似 上述代码段,只是把和HttpContext.Current.Cache  有关的地方全部改为Redis 相关语句。

至此结束,实现请求量限制。

经验:关于HttpContext.Current.Cache,有些代码看似平淡但是内部有说不清楚的作用。

以后还是要多写写代码,多积累经验,遇到的坑多了,才会吃一堑长一智。当然,若能得到高人指点或者自己之前在其他地方见过这个坑的相关介绍,那么能避免再好不过,少走弯路节省时间。   至于,多个请求同时请求、并发、共享资源的事情要小心,最好加锁(加锁会导致效率变差),这也是设计时要考虑好的,不然出了BUG难以调试重现出来。

时间: 2024-10-27 12:19:04

请求量限制方法-使用本地Cache记录当前请求量[坑]的相关文章

转载:30多条mysql数据库优化方法,千万级数据库记录查询轻松解决

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描, Sql 代码 : select id from t where num is null; 可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询: Sql 代码 : select id from t where num=0; 3.应尽量避免在 wh

无法从使用方法中推导出方法... 的类型实參,请尝试显式指定类型实參

这个问题,网上基本没得什么解决方法,事实上都是编程习惯造成的,在程序的世界里,用户自己命名必须规范,唯一,与系统框架提供的对象名称分开.否则将会产生非常多,标题问题.以上问题,非常多都是没有确切 指定,多空间命名的时候,建立了多个一样的对象名,而在统一地方使用对象,没有明白指定,哪个空间对象,就会报以上错误:比如 空间一 using System; namespase Models { public class orderby {} } 空间二 using System; namespase B

JAVA方法和本地方法(转载)

转载自:http://blog.sina.com.cn/s/blog_5b9b4abe01016zw0.html JAVA中有两种方法:JAVA方法和本地方法 JAVA方法是由JAVA编写的,编译成字节码,存储在class文件中 本地方法是由其它语言编写的,编译成和处理器相关的机器代码 本地方法保存在动态链接库中,即.dll(windows系统)文件中,格式是各个平台专有的 JAVA方法是与平台无关的,但是本地方法不是 运行中的JAVA方法调用本地方法时,虚拟机装载包含这个本地方法的动态库的,并

错误: 在类 com.zs.container.CollectionData 中找不到主方法, 请将主方法定义为: public static void main(String[] args)

错误: 在类 com.zs.container.CollectionData 中找不到主方法, 请将主方法定义为: public static void main(String[] args) package com.zs.container; import java.util.ArrayList; import com.java.array.generator.CountingGenerator.String; import com.java.array.generator.CountingG

错误: 在类 Main 中找不到 main 方法, 请将 main 方法定义为: public static void main(String[] args) 否则 JavaFX 应用程序类必须扩展javafx.application.Application

错误: 在类 Main 中找不到 main 方法, 请将 main 方法定义为: public static void main(String[] args)否则 JavaFX 应用程序类必须扩展javafx.application.Application 出现这种错误的原因其中一种就是 导包时错把其他的String包导入,以至于找不到main(String[ ]  args) 原文地址:https://www.cnblogs.com/mibloom/p/9497357.html

HTTP 错误 404.3 – Not Found 由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。

今天,在vs2013中新建了一个placard.json文件,当我用jq读取它的时候,去提示404,直接在浏览器访问这个文件,提示: HTTP 错误 404.3 – Not Found 由于扩展配置问题而无法提供您请求的页面.如果该页面是脚本,请添加处理程序.如果应下载文件,请添加 MIME 映射. 解决方案:进入IIS

HTTP 错误 404.3 - Not Found 由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。

解决问题:由于扩展配置问题而无法提供您请求的页面.如果该页面是脚本,请添加处理程序.如果应下载文件,请添加 MIME 映射. WindowServer2012服务器,添加角色安装完.netframework和iis之后,运行aspx页面就报如下错误: HTTP 错误 404.3 - Not Found 由于扩展配置问题而无法提供您请求的页面.如果该页面是脚本,请添加处理程序.如果应下载文件,请添加 MIME 映射. ?可能是缺少处理程序映射.默认情况下,静态文件处理程序将处理所有内容.?您要使用

IIS中预览错误问题的解决办法(HTTP 错误 404.3 - Not Found 由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射 )

控制面板 程序 打开或关闭windows程序 Internet信息服务 万维网服务 应用程序开发功能 勾选.net  扩展性    ASP    ASP.net 按照这个顺序就可以解决该问题. IIS中预览错误问题的解决办法(HTTP 错误 404.3 - Not Found 由于扩展配置问题而无法提供您请求的页面.如果该页面是脚本,请添加处理程序.如果应下载文件,请添加 MIME 映射 ),布布扣,bubuko.com

解决问题:由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。

WindowServer2012服务器,添加角色安装完.netframework和iis之后,运行aspx页面就报如下错误: HTTP 错误 404.3 - Not Found 由于扩展配置问题而无法提供您请求的页面.如果该页面是脚本,请添加处理程序.如果应下载文件,请添加 MIME 映射. ?可能是缺少处理程序映射.默认情况下,静态文件处理程序将处理所有内容.?您要使用的功能可能尚未安装.?没有为网站或应用程序启用相应的 MIME 映射.(警告: 请不要为用户不应下载的 .ASPX 页或 .c