ServiceStack.Redis实现Redis缓存的速率限制

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ntong.RL;

namespace Ntong.RL.Test
{
    class Program
    {
        static void Main(string[] args)
        {

            Limiter RateLimiter = new Limiter();

            for (int i = 0; i < 25; i++)
            {

                try
                {
                    //do stuff
                    RateLimiter.CheckLimit("GetMyItems");
                    Console.WriteLine("调用 完成");
                }
                catch (Exception exc)
                {
                    Console.WriteLine(exc.Message);
                } 
            }

            Console.ReadLine();

        }
    }
}

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="RL.RedisHost" value="127.0.0.1"/>
    <add key="RL.RedisPort" value="6379"/>
    <add key="RL.PerSecondRateLimit" value="10"/>
    <add key="RL.PerMinuteRateLimit" value="20"/>
    <add key="RL.PerHourRateLimit" value="50"/>
  </appSettings>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
</configuration>

Limiter.cs

using System;
using RC.RateLimit.Common;
using ServiceStack.Redis;

namespace Ntong.RL
{
    public class Limiter
    {

        string redisHost = Config.RedisHost;

        int redisPort = Config.RedisPort;
        int perSecondLimit = Config.PerSecondRateLimit;
        int perMinuteLimit = Config.PerMinuteRateLimit;

        int perHourLimit = Config.PerHourRateLimit;

        public RedisClient redisCl;

        public Limiter()
        {
            redisCl = new RedisClient(redisHost, redisPort,"123456");

        }


        public void CheckLimit(string key)
        {

            try
            {
                PerSecondLimiter(redisCl, key, perSecondLimit);
                PerMinuteLimiter(redisCl, key, perMinuteLimit);
                PerHourLimiter(redisCl, key, perHourLimit);

            }
            catch (ServiceStack.Redis.RedisException ex)
            {
                throw ex;
            }

        }


        public void PerSecondLimiter(RedisClient client, string key, int limit)
        {
            key += ":Second";

            //Redis Llen 命令用于返回列表的长度。 
            //如果列表 key 不存在,则 key 被解释为一个空列表,返回 0 。 
            //如果 key 不是列表类型,返回一个错误。 
            long rqs = client.LLen(key);
            if (rqs <= limit)
            {
                //LPush 命令将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。
                //注意:在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value 值。
                client.LPush(key, BitConverter.GetBytes(DateTime.Now.Ticks)); //DateTime.Now.Ticks 是指从DateTime.MinValue之后过了多少时间,10000000为一秒,保存在long类型的变量里,可以将它传到datetime的构造函数内转成时间类型。
            }
            else
            {
                //LIndex 命令用于通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 
                DateTime time = new DateTime(BitConverter.ToInt64(client.LIndex(key, -1),0));
                if (DateTime.Now - time < new TimeSpan(0, 0, 1))  //指定hours, minutes,和seconds转换为个时钟周期,并且值初始化此实例。
                {
                    throw new RedisException("超过每秒速率限制.");
                }
                else
                {
                    client.LPush(key, BitConverter.GetBytes(DateTime.Now.Ticks));
                }
            }

            client.LTrim(key, 0, limit);

        }


        public void PerMinuteLimiter(RedisClient client, string key, int limit)
        {
            key += ":Minute";

            long rqs = client.LLen(key);
            if (rqs <= limit)
            {
                client.LPush(key, BitConverter.GetBytes(DateTime.Now.Ticks));
            }
            else
            {
                DateTime time = new DateTime(BitConverter.ToInt64(client.LIndex(key, -1), 0));
                if (DateTime.Now - time < new TimeSpan(0, 1, 0))
                {
                    throw new RedisException("超过每分钟速率限制.");
                }
                else
                {
                    client.LPush(key, BitConverter.GetBytes(DateTime.Now.Ticks));
                }
            }

            client.LTrim(key, 0, limit);

        }


        public void PerHourLimiter(RedisClient client, string key, int limit)
        {
            key += ":Hour";

            long rqs = client.LLen(key);
            if (rqs <= limit)
            {
                client.LPush(key, BitConverter.GetBytes(DateTime.Now.Ticks));
            }
            else
            {
                DateTime time = new DateTime(BitConverter.ToInt64(client.LIndex(key, -1),0));
                if (DateTime.Now - time < new TimeSpan(1, 0, 0))
                {
                    throw new RedisException("超出每小时速率限制.");
                }
                else
                {
                    client.LPush(key, BitConverter.GetBytes(DateTime.Now.Ticks));
                }
            } 
            client.LTrim(key, 0, limit); 
        } 
    } 
}

Config.cs

using System;
using System.Data;
using static System.Int32;

namespace Ntong.RL.Common
{
    public class Config
    {
        public static string RedisHost => ReadConfiguration("RateLimit.RedisHost", "");

        public static int RedisPort => Parse(ReadConfiguration("RateLimit.RedisPort", ""));

        public static int PerSecondRateLimit => Parse(ReadConfiguration("RateLimit.PerSecondRateLimit", ""));

        public static int PerMinuteRateLimit => Parse(ReadConfiguration("RateLimit.PerMinuteRateLimit", ""));

        public static int PerHourRateLimit => Parse(ReadConfiguration("RateLimit.PerHourRateLimit", ""));

        public static string ReadConfiguration(string key, string defaultValue = null)
        {
            var keyVal = System.Configuration.ConfigurationManager.AppSettings[key];
            return !string.Equals(keyVal, null, StringComparison.Ordinal) ? keyVal : defaultValue;
        }
    }
}

运行结果如图:

ServiceStack.Redis实现Redis缓存的速率限制_第1张图片

你可能感兴趣的:(Nosql之Redis数据库)