FrequencyControler.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. using System;
  2. using System.Web;
  3. namespace SiteCore
  4. {
  5. public class FrequencyControler
  6. {
  7. /// <summary>
  8. /// 访问控制器名称,用于区分其它控制器,支持多个控制器
  9. /// </summary>
  10. private string Name { get; set; }
  11. /// <summary>
  12. /// 限定时长
  13. /// </summary>
  14. private int Seconds { get; set; }
  15. /// <summary>
  16. /// 限定次数
  17. /// </summary>
  18. private int Times { get; set; }
  19. public readonly int MAX_TIMES = 100;
  20. #region 私有方法
  21. private string SessionNameDatelist
  22. {
  23. get { return String.Format("fc.{0}.datelist", Name); }
  24. }
  25. private string SessionNameDatepos
  26. {
  27. get { return String.Format("fc.{0}.datepos", Name); }
  28. }
  29. /// <summary>
  30. /// 取得用于保存每次访问时间点的数组(做队列用)
  31. /// </summary>
  32. /// <returns></returns>
  33. private long[] GetDateList()
  34. {
  35. if (HttpContext.Current.Session[SessionNameDatelist] == null)
  36. {
  37. HttpContext.Current.Session[SessionNameDatelist] = new long[MAX_TIMES];
  38. }
  39. return (long[])HttpContext.Current.Session[SessionNameDatelist];
  40. }
  41. /// <summary>
  42. /// 获取时间记录位置,相当于当前队列位置
  43. /// </summary>
  44. /// <returns></returns>
  45. private int GetDatepos()
  46. {
  47. if (HttpContext.Current.Session[SessionNameDatepos] == null)
  48. {
  49. HttpContext.Current.Session[SessionNameDatepos] = 0;
  50. }
  51. return (int)HttpContext.Current.Session[SessionNameDatepos];
  52. }
  53. /// <summary>
  54. /// 设置时间记录位置,相当于当前队列位置
  55. /// </summary>
  56. /// <param name="pos"></param>
  57. private void SetDatepos(int pos)
  58. {
  59. HttpContext.Current.Session[SessionNameDatepos] = pos;
  60. }
  61. #endregion
  62. /// <summary>
  63. /// 构造访问检测器,限定指定时间内最多请求次数
  64. /// </summary>
  65. /// <param name="name">名称</param>
  66. /// <param name="seconds">限定时间范围(秒数)</param>
  67. /// <param name="times">限定次数</param>
  68. public FrequencyControler(string name, int seconds, int times)
  69. {
  70. Name = name;
  71. Seconds = seconds;
  72. Times = times;
  73. if (times > MAX_TIMES) throw new Exception("限定次数设置值超出上限!");
  74. }
  75. /// <summary>
  76. /// 记录一次访问,在时间点数组的下一个位置(按最大长度循环覆盖存储)
  77. /// </summary>
  78. public void Record()
  79. {
  80. long[] ds = GetDateList();
  81. int pos = GetDatepos();
  82. pos = (pos + 1) % ds.Length;
  83. ds[pos] = DateTime.Now.Ticks;
  84. SetDatepos(pos);
  85. }
  86. /// <summary>
  87. /// 判断是否达到访问限制(并默认记录一次请求)
  88. /// </summary>
  89. /// <returns></returns>
  90. public bool IsTooFrequently()
  91. {
  92. return IsTooFrequently(true);
  93. }
  94. /// <summary>
  95. /// 判断是否达到访问限制(主要外部使用功能)
  96. /// </summary>
  97. /// <param name="isRecord">是否包含本次请求,为true时,会先记录一次请求再判断</param>
  98. /// <returns></returns>
  99. public bool IsTooFrequently(bool isRecord)
  100. {
  101. if (isRecord) Record();
  102. long[] ds = GetDateList();
  103. int pos = GetDatepos();
  104. // 取得之前 限定次数 位置的时间点与最后的时间点比较,
  105. // 如果之前的访问次数还没有达到限定次数则忽略
  106. int pre_pos = (pos + ds.Length - Times + 1) % ds.Length;
  107. long pre_ticks = ds[pre_pos];
  108. long now_ticks = ds[pos];
  109. // 如果访问的次烤都还没有达到限定次数,pre_ticks 必定为0,所以大于0时才进行比较
  110. if (pre_ticks > 0)
  111. {
  112. TimeSpan span = new TimeSpan(now_ticks - pre_ticks);
  113. if (span.TotalSeconds <= Seconds)
  114. {
  115. return true;
  116. }
  117. }
  118. return false;
  119. }
  120. }
  121. }