Eclipse下FatJar插件的安装与利用葡京在线开户

未来,因为种种因素,你必须对2个呼吁恐怕措施开始展览频率上的拜会限制。
诸如,
你对外提供了二个API接口,注册用户每分钟最多能够调用100回,非注册用户每分钟最多能够调用十四次。
诸如,
有八个要命吃服务器能源的主意,在平等时刻不能跨越10民用调用那一个法子,不然服务器满载。
譬如, 有一部分分化平常的页面,访客并不可能反复的造访或发言。
譬如说, 秒杀活动等进行。
譬如
,防患DDOS,当达到一定频率后调用脚本iis服务器ip黑名单,防火墙黑名单。
如上各个的比方,也便是说,怎么样从一个断面包车型地铁角度对调用的方法开始展览频率上的限制。而对功能限制,服务器层面都有最直接的缓解格局,未来作者说的则是代码层面上的频率管理控制。

在Eclipse下生成jar包分很多种情况。最简便的景色是不曾用到第2方jar包,那么直接Export就足以生成jar包。可是假诺用到了第①方jar包,那么就相比麻烦了,很不便利。FatJar能够化解那些题材,使用FatJar,固然带有了众多第叁方jar包,也得以很便宜的生成jar包。

正文给出三个示范,多少个是依照单机环境的实现,第3个则是据书上说分布式的Redis实现

安装FatJar的点子有二种,能够下载文件,复制到Eclipse的plugin文件夹下,也能够在线安装。上面介绍在线安装的步调。


(1)在Eclipse里面点击Help->Install New
Software,在弹出的Install界面里面,点击Add,输入名称Fat
Jar,和地点http://kurucz-grafika.de/fatjar,然后就可以自动安装了;

以率先个API接口须要为例,先说下单机环境下的兑现。
安分守纪惯性思维,大家当然会想到缓存的逾期策略那种方法,然而严俊来讲就HttpRuntime.Cache而言,通过缓存的过期策略来对请求进行频率的出现控制是不合适的。
  HttpRuntime.Cache
是应用程序级别的Asp.Net的缓存技术,通过那个技能能够表明多个缓存对象,能够为各样对象设置过期时间,当过期时刻到达后该缓存对象就会消亡(约等于当你拜访该对象的时候为Null)

葡京在线开户 1

  为何这么说吧?比如对有个别方法(方法名:GetUserList)大家要拓展1秒钟最多十三遍的范围,未来我们就新建1个int型的Cache对象,然后设置1分钟后过期消失。那么每当访问GetUserList方法前,大家就先判断那些Cache对象的值是不是高于10,如若超出10就不进行GetUserList方法,假使低于10则允许实施。每当访问该指标的时候假诺不存在或许逾期就新建,那样循环,则该指标永远不恐怕跨越10。

(2)安装成功今后,点击Windows->Preferences,弹出的窗口里面若是有Fat
Jar Preferences,则申明安装成功;

1   if ((int)HttpRuntime.Cache["GetUserListNum"] > 10) //大于10请求失败
2   {
3      Console.WriteLine("禁止请求");
4   }
5   else
6   {
7      HttpRuntime.Cache["GetUserListNum"] = (int)HttpRuntime.Cache["GetUserListNum"] + 1; //否则该缓存对象的值+1
8      Console.WriteLine("允许请求");
9   }

葡京在线开户 2

那般的盘算及落到实处绝对来说卓殊简单,不过根据那样的3个模型设定,那么就会现出那种状态:

设置好Fat
Jar以往,就能够便宜的对含有第壹方jar包的工程生成Jar包了。

 葡京在线开户 3

(1)右键点击必要打包的工程,点击Export,在弹出的对话框中挑选Other->Fat
Jar Exporter;

 

葡京在线开户 4

如上图,种种点代表2回访问请求,笔者在0秒的时候
新建了三个名字为GetUserListNum的缓存对象。
在0~0.5秒之内
小编访问了二遍,在0.5~1秒之内,大家走访了八遍。此时,该目的消失,然后大家随后访问,该对象重置为0.
              
 在第1~1.5秒之内,仍然访问了八次,在第1.5秒~2秒之内做客了贰遍。

(2)点击Next,选取要打包的工程;

听闻那种简单缓存过期策略的模型,在那2分钟内,大家即使平均每秒钟都访问了十二遍,满意那么些规定,然而即便大家从中取八个之内段,0.5秒~1.5秒之内,也是1分钟,不过却的确的造访了11次!远远超越了我们设置的
1分钟最多访问13次的 限制。

葡京在线开户 5

 

(3)点击Next,输入Jar
Name,要是想在随飞机地点置生成Jar包,选拔use extern
Jar-Name,采取Main-Class,Class-Path不填,选用One-JA宝马X5;

那正是说怎么样科学的来化解地点的标题吧?大家可以因而模拟对话级别的信号量这一招数,那也正是我们前天的宗旨了。
   什么是信号量?仅就以代码而言,  static
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(5); 
它的意趣就象征在八线程情状下,在任何权且刻,只好同时四个线程去拜谒。

葡京在线开户 6

 

(4)点击Next,能够看看该工程用到的第1方Jar包,全体打勾;

4容器4线程模型

到现在,在落到实处代码的事先我们先规划2个模子。

葡京在线开户 7

  如若大家有一个用户A的管道,这些管道里装着用户A的伏乞,比如用户A在一分钟发(英文名:zhōng fā)出了1捌次呼吁,那么每2个呼吁过来,管道里的成分都会多三个。不过我们设定这么些管道最多只好容纳拾二个因素,而且每一个成分的存活期为1秒,1秒后则该因素消失。那么如此设计的话,无论是速率照旧多少的突进,都会有管道长度的界定。这样一来,无论从哪2个时日节点依然时间间隔出发,那么些管道都能满足大家的频率限制须求。

而那里的管道,就非得和平谈判会议话Id来对号入座了。每当有新会话进来的时候就生成1个新管道。这几个会话id依照自个儿场景所定,能够是sessionId,能够是ip,也能够是token。

那么既然那一个管道是会话级其余,大家必然得须求叁个容器,来装那么些管道。未来,我们以IP来命名会话管道,并把具有的管道都装载在二个容器中,如图

葡京在线开户 8

而听新闻说刚才的设定,大家还亟需对容器内的每条管道的因素进行拍卖,把过期的给删除掉,为此,还索要单独为该容器开辟出二个线程来为每条管道展开成分的清理。而当管道的因素为0时,大家就清掉该管道,以便节省容器空间。

 葡京在线开户 9

自然,由于用户量多,叁个容器内可能存在上万个管道,那么些时候偏偏用3个容器来装载来清理,在效能上明明是不够的。这一个时候,大家就得对容器进行横向增加了。

  比如,大家能够依据Cpu宗旨数自动生成对应的数量的容器,然后遵照2个算法,对IP来进行导流。小编当下cpu是6个逻辑主旨,就生成了三个容器,每当用户访问的时候,都会起始经过二个算法,那些算法会对IP实行拍卖,如192.168.1.11~192.168.1.13以此Ip段进第十二个容器,xxx~xxx进第二个容器,依次类推,相应的,也就有了几个线程去分别处理6个容器中的管道。

葡京在线开户 10

 

那么,最后就形成了笔者们的4容器4线程模型了。

方今,着眼于编码完成:

  首先我们供给2个能承载那么些器皿的载体,那几个载体类似于连接池的概念,能够遵照一些内需自动生成适应数量的器皿,如若有特殊要求的话,仍是可以在容器上切出3个容器管理的面,在线程上切出2个线程管理的面以便于实时监督和调度。假诺真要做那样多少个种类,那么
容器的调度 和 线程的调度功效是不可或缺的,而本德姆o则是做到了严重性意义,像容器和线程在代码中小编也没剥离开来,算法也是一向写死的,实际设计中,对算法的规划照旧很要紧的,还有四线程模型中,怎么着上锁才能让作用最大化也是重点的。

而那里为了案例的直观就直接写死成四个容器。

public static List<Container> ContainerList = new List<Container>(); //容器载体
static Factory()
{
     for (int i = 0; i < 4; i++)
     {
        ContainerList.Add(new Container(i));  //遍历4次  生成4个容器
     }
     foreach (var item in ContainerList)
     {
        item.Run();    //开启线程
     }
}

到现在,我们只要 有编号为 0 到 40 那样的 4一个用户。那么这几个导流算法
小编也就间接写死,编号0至9的用户
将他们的伸手给抛转到第②个容器,编号10~19的用户
放到第二个容器,编号20~29放到第⑩个容器,编号30~40的用户放到第多少个容器。

那么那一个代码就是那样的:

 static Container GetContainer(int userId, out int i) //获取容器的算法
 {
     if (0 <= userId && userId < 10)    //编号0至9的用户  返回第一个容器  依次类推
     {
          i = 0;
          return ContainerList[0];
     }
     if (10 <= userId && userId < 20)
     {
          i = 1;
          return ContainerList[1];
     }
     if (20 <= userId && userId < 30)
     {
          i = 2;
          return ContainerList[2];
      }
      i = 3;
      return ContainerList[3];
  }

当咱们的对话请求经过算法的导流之后,都必须调用三个办法,用于辨别管道数量。若是管道数量已经超(Jing Chao)出10,则请求战败,不然成功

  public static void Add(int userId)
  {
       if (GetContainer(userId, out int i).Add(userId))
            Console.WriteLine("容器" + i + " 用户" + userId + "  发起请求");
       else
            Console.WriteLine("容器" + i + " 用户" + userId + "  被拦截");
  }

接下去正是容器Container的代码了。

那边,对容器的选型用线程安全的ConcurrentDictionary类。
  线程安全:当四个线程同时读写同二个共享成分的时候,就会出现数量错乱,迭代报错等安全问提
  ConcurrentDictionary:除了GetOrAdd方法要慎用外,是.Net4.0专为消除Dictionary线程安全而出的新类型
  ReaderWriterLockSlim:较ReaderWriterLock优化的读写锁,多少个线程同时做客读锁
或  二个线程访问写锁

private ReaderWriterLockSlim obj = new ReaderWriterLockSlim();  //在每个容器中申明一个读写锁
public ConcurrentDictionary<string, ConcurrentList<DateTime>> dic = new ConcurrentDictionary<string, ConcurrentList<DateTime>>(); //创建该容器 dic

然后当你向容器添加一条管道中的数据是经过这一个法子:

 public bool Add(int userId)
 {
     obj.EnterReadLock();//挂读锁,允许多个线程同时写入该方法
     try
     {
         ConcurrentList<DateTime> dtList = dic.GetOrAdd(userId.ToString(), new ConcurrentList<DateTime>()); //如果不存在就新建 ConcurrentList
         return dtList.CounterAdd(10, DateTime.Now); //管道容量10,当临界管道容量后 返回false
     }
     finally
     {
         obj.ExitReadLock();
     }
 }

 那里,为了在后头的线程遍历删除ConcurrentList的管道的时候保险ConcurrentList的安全性,所以那边要加读锁。

 而ConcurrentList,因为.Net没有推出List集合类的线程安全(count和add加锁),所以本身新建了二个接续于List<T>的广安项目,在那里
封装了1个须求使用的艺术。

public class ConcurrentList<T> : List<T>
{
    private object obj = new object();

    public bool CounterAdd(int num, T value)
    {
        lock (obj)
        {
            if (base.Count >= num)
                return false;
            else
                base.Add(value);
            return true;
        }
    }
    public new bool Remove(T value)
    {
        lock (obj)
        {
            base.Remove(value);
            return true;
        }
    }
    public new T[] ToArray() 
    {
        lock (obj)
        {
            return base.ToArray();
        }
    }
}

末段便是线程的周转格局:

 public void Run()
 {
     ThreadPool.QueueUserWorkItem(c =>
     {
         while (true)
         {
             if (dic.Count > 0)
             {
                 foreach (var item in dic.ToArray())
                 {
                     ConcurrentList<DateTime> list = item.Value;
                     foreach (DateTime dt in list.ToArray())   
                     {
                         if (DateTime.Now.AddSeconds(-3) > dt)
                         {
                             list.Remove(dt);
                             Console.WriteLine("容器" + seat + " 已删除用户" + item.Key + "管道中的一条数据");
                         }
                     }
                     if (list.Count == 0)
                     {
                         obj.EnterWriteLock();
                         try
                         {
                             if (list.Count == 0)
                             {
                                 if (dic.TryRemove(item.Key, out ConcurrentList<DateTime> i))
                                 { Console.WriteLine("容器" + seat + " 已清除用户" + item.Key + "的List管道"); }
                             }
                         }
                         finally
                         {
                             obj.ExitWriteLock();
                         }
                     }
                 }

             }
             else
             {
                 Thread.Sleep(100);
             }
         }
     }
   );
 }

最后,是成效图,3个是依据控制台的,还一个是依照Signalr的。

 葡京在线开户 11葡京在线开户 12

葡京在线开户 13

分布式下Redis

上面介绍了一种频率限制的模子,分布式与单机比较,无非正是载体不一致,我们假如把这么些容器的载体从程序上移植出来,来弄成3个独自的服务大概直接借用Redis也是可行的。

此处就介绍分布式情形下,Redis的完毕。

不相同于Asp.Net的八线程模型,大概因为Redis的各体系型的要素十分粒度的操作造成各个加锁的纷纷,所以在网络请求处理那块Redis是单线程的,基于Redis的落到实处则因为单线程的缘故在编码角度不用太多着想到与逻辑无关的难题。

  容易介绍下,Redis是二个内部存款和储蓄器数据库,这一个数据库属于非关系型数据库,它的概念区别于一般的大家体会的Mysql
Oracle
SqlServer关系型数据库,它没有Sql没有字段名从未表名那一个概念,它和HttpRunTime.Cache的概念大约一样,首先从操作上属于键值对形式,就好像Cache[“键名”]
那样就能取获得值类似,而且能够对每一种Key设置过期策略,而Redis中的Key所对应的值并不是想存啥就存啥的,它帮助各类数据类型:string(字符串),hash(哈希),list(列表),set(集合)及sorted
set(有序聚集)。

后天要说的是Sorted
set有序聚集,有序聚集相比较别的的联谊类型的特殊点在于,使用有序聚集的时候还是可以给插入的成分内定二个积分score,大家把那些积分score精通为排系列,它个中会对积分实行排序,积分允许再度,而有序聚集中的成分则是绝无仅有。

  照旧一如既往的笔触,每当有用户访问的时候,都对该用户的
管道(有序聚集)中添加1个要素,然后设置该因素的积分为近期些天子。接着在先后中开个线程,来对管道中积分小于约定时间的要素举办清理。因为规定有序聚集中的成分只好是绝无仅有值,所以在赋值方面倘若是满意uuid即可。

 葡京在线开户 14

那正是说用Redis来兑现的代码那就是近乎那种:

葡京在线开户 15

透过using语法糖完毕IDisposable而卷入的Redis分布式锁,然后里面不荒谬的逻辑判断。

那样的代码固然也能做到功用,但不够本身。Redis是个基于内部存款和储蓄器的数据库,于质量而言,瓶颈在于网络IO 上,与Get2次发出一回呼吁比较,能还是不能够通过一段脚本来达成多数逻辑吗?

有的,Redis支持 Lua脚本:
  Lua
是一种轻量小巧的脚本语言,用规范C语言编写并以源代码情势开放,
其布署指标是为着放置应用程序中,从而为应用程序提供灵活的扩张和定制功用。
  大约意思便是,间接向Redis发送一段脚本也许让它直接本地读取一段脚本从而一向促成全体的逻辑。

/// <summary>
/// 如果 大于10(AccountNum) 就返回1   否则就增加一条集合中的元素 并返回 空
/// </summary>
/// <param name="zcardKey"></param>
/// <param name="score"></param>
/// <param name="zcardValue"></param>
/// <param name="AccountNum"></param>
/// <returns></returns>
public string LuaAddAccoundSorted(string zcardKey, double score, string zcardValue, int AccountNum)
{
    string str = "local uu = redis.call('zcard',@zcardKey) if (uu >=tonumber(@AccountNum)) then return 1 else redis.call('zadd',@zcardKey,@score,@zcardValue)  end";
    var re = _instance.GetDatabase(_num).ScriptEvaluate(LuaScript.Prepare(str), new { zcardKey = zcardKey, score = score, zcardValue = zcardValue, AccountNum=AccountNum });
    return re.ToString();
}

local
uu就是发美赞臣(Meadjohnson)个为名uu的变量的意味,redis.call正是redis命令,那段脚本意思正是只要
大于10(AccountNum) 就回去1   否则就扩展一条集合中的成分 并赶回 空。

管道内元素处理的艺术正是:

 /// <summary>
 /// 遍历当前所有前缀的有序集合,如果数量为0,那么就返回1 否则 就删除 满足最大分值条件区间的元素,如果该集合个数为0则消失
 /// </summary>
 /// <param name="zcardPrefix"></param>
 /// <param name="score"></param>
 /// <returns></returns>
public string LuaForeachRemove(string zcardPrefix, double score)
 {
     StringBuilder str = new StringBuilder();
     str.Append("local uu = redis.call('keys',@zcardPrefix) "); //声明一个变量 去获取 模糊查询的结果集合
     str.Append("if(#uu==0) then");    //如果集合长度=0
     str.Append("   return 1 ");
     str.Append("else ");
     str.Append("   for i=1,#uu do ");   //遍历
     str.Append("       redis.call('ZREMRANGEBYSCORE',uu[i],0,@score) ");  //删除从0 到 该score 积分区间的元素
     str.Append("       if(redis.call('zcard',uu[i])==0) then ");  //如果管道长度=0
     str.Append("           redis.call('del',uu[i]) ");   //删除
     str.Append("       end ");
     str.Append("   end ");
     str.Append("end ");
     var re = _instance.GetDatabase(_num).ScriptEvaluate(LuaScript.Prepare(str.ToString()), new { zcardPrefix = zcardPrefix + "*", score = score });
     return re.ToString();

那2段代码通过发送Lua脚本的形式来形成了全方位经过,因为Redis的网络模型原因,所以把LuaForeachRemove方法给建议来做个服务来单独处理即可。至于那种多容器三十二线程的得以实现,则完全能够开八个Redis的实例来兑现。最后放上效果图。

葡京在线开户 16

最后,作者把那么些都给做成了个德姆o。不过尚未找到确切的上传网盘,所以大家能够留邮箱(留了就发),大概直接加QQ群众文化艺术件自取,探讨调换:166843154

 

自个儿喜欢和本人同一的人交朋友,不被环境影响,自个儿是投机的名师,欢迎加群
.Net web交换群, QQ群:166843154 欲望与挣扎

 

作者:小曾
出处:http://www.cnblogs.com/1996V/p/8127576.html 欢迎转载,但任何转载必须保留完整文章及博客园出处,在显要地方显示署名以及原文链接。
.Net交流群, QQ群:166843154 欲望与挣扎 

(5)点击Finish,达成打包;

葡京在线开户 17

(6)在Dos窗口下运维Jar包,成功。

葡京在线开户 18