|
|
51CTO旗下网站
|
|
移动端

生活中随处可见的限流,在Java中又是怎么应用的呢?

回到代码世界上也是一样的,服务器能处理的请求数有限,如果请求量特别大,我们需要做限流(要么就让请求等待,要么就把请求给扔了)

作者:Java之高级架构来源:今日头条|2019-09-20 09:37

【大咖·来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》

 

一、限流基础知识介绍

为啥要限流,相信就不用我多说了。

  • 比如,我周末去饭店吃饭,但是人太多了,我只能去前台拿个号,等号码到我的时候才能进饭店吃饭。如果饭店没有限流怎么办?一到饭点,人都往里冲,而饭店又处理不了这么多人流,很容易就出事故(饭店塞满了人,无路可走。饭店的工作人员崩溃了,处理不过来)
  • 回到代码世界上也是一样的,服务器能处理的请求数有限,如果请求量特别大,我们需要做限流(要么就让请求等待,要么就把请求给扔了)

在代码世界上,限流有两种比较常见的算法:

  • 令牌桶算法
  • 漏桶算法

1.1 什么是漏桶算法

比如,现在我有一个桶子,绿色那块是我能装水的容量,如果超过我能装下的容量,再往桶子里边倒水,就会溢出来(限流):

生活中随处可见的限流,在Java中又是怎么应用的呢?

我们目前可以知道的是:

  • 桶子的容量是固定的(是图上绿色那块)
  • 超出了桶子的容量就会溢出(要么等待,要么直接丢弃)

OK,现在我们在桶子里挖个洞,让水可以从洞子里边流出来:

生活中随处可见的限流,在Java中又是怎么应用的呢?

桶子的洞口的大小是固定的,所以水从洞口流出来的速率也是固定的。

所以总结下来算法所需的参数就两个:

  • 桶子的容量
  • 漏水的速率

漏桶算法有两种实现:

  • 不允许突发流量的情况:如果进水的速率大于出水的速率,直接舍弃掉多余的水。比如,我的桶子容量能装100L,但我的桶子出水速率是10L/s。此时,如果现在有100L/s的水进来,我只让10L的水进到桶子,其余的都限流。(限定了请求的速度)
  • 允许一定的突发流量情况:我的桶子能装100L,如果现在我的桶子是空的,那么这100L的水都能进我的桶子。我以10L/s的速率将这些水流出,如果还有100L的水进来,只能限流了。

经过上面的分析我们就知道:

漏桶算法可以平滑网络上的突发流量(因为漏水的速率是固定的)

1.2 什么是令牌桶算法

现在我有另外一个桶子,这个桶子不用来装水,用来装令牌:

生活中随处可见的限流,在Java中又是怎么应用的呢?

令牌会一定的速率扔进桶子里边,比如我1秒扔10个令牌进桶子:

生活中随处可见的限流,在Java中又是怎么应用的呢?

桶子能装令牌的个数有上限的,比如我的桶子最多只能装1000个令牌。

每个请求进来,就会去桶子拿一个令牌

  • 比如这秒我有1001个请求,我就去桶子里边拿1001个令牌,此时可能会出现两种情况:
  • 桶子里边没有1001个令牌,只有1000个,那没拿到令牌的请求只能被阻塞了(等待)
  • 桶子里边有1001个令牌,所有请求都可以执行。
生活中随处可见的限流,在Java中又是怎么应用的呢?

令牌桶算法支持网络上的突发流量

**漏桶和令牌桶的区别:**从上面的例子估计大家也能看出来了,漏桶只能以固定的速率去处理请求,而令牌桶可以以桶子最大的令牌数去处理请求

二、RateLimiter使用

RateLimiter是Guava的一个限流组件,我这边的系统就有用到这个限流组件,使用起来十分方便。

引入pom依赖:

  1. <dependency> 
  2.  <groupId>com.google.guava</groupId> 
  3.  <artifactId>guava</artifactId> 
  4.  <version>20.0</version> 
  5. </dependency> 

RateLimiter它是基于令牌桶算法的,API非常简单,看以下的Demo:

  1. public static void main(String[] args) { 
  2.  //线程池 
  3.  ExecutorService exec = Executors.newCachedThreadPool(); 
  4.  //速率是每秒只有3个许可 
  5.  final RateLimiter rateLimiter = RateLimiter.create(3.0); 
  6.  for (int i = 0; i < 100; i++) { 
  7.  final int no = i; 
  8.  Runnable runnable = new Runnable() { 
  9.  @Override 
  10.  public void run() { 
  11.  try { 
  12.  //获取许可 
  13.  rateLimiter.acquire(); 
  14.  System.out.println("Accessing: " + no + ",time:" 
  15.  + new SimpleDateFormat("yy-MM-dd HH:mm:ss").format(new Date())); 
  16.  } catch (Exception e) { 
  17.  e.printStackTrace(); 
  18.  } 
  19.  } 
  20.  }; 
  21.  //执行线程 
  22.  exec.execute(runnable); 
  23.  } 
  24.  //退出线程池 
  25.  exec.shutdown(); 
  26.  } 

我们可以从结果看出,每秒只能执行三个:

生活中随处可见的限流,在Java中又是怎么应用的呢?

三、分布式限流

RateLimiter是一个单机的限流组件,如果是分布式应用的话,该怎么做?

可以使用Redis+Lua的方式来实现,大致的lua脚本代码如下:

  1. local key = "rate.limit:" .. KEYS[1] --限流KEY 
  2. local limit = tonumber(ARGV[1]) --限流大小 
  3. local current = tonumber(redis.call('get'keyor "0"
  4. if current + 1 > limit then --如果超出限流大小 
  5.  return 0 
  6. else --请求数+1,并设置1秒过期 
  7.  redis.call("INCRBY"key,"1"
  8.  redis.call("expire"key,"1"
  9.  return current + 1 

Java代码如下:

  1. public static boolean accquire() throws IOException, URISyntaxException { 
  2.  Jedis jedis = new Jedis("127.0.0.1"); 
  3.  File luaFile = new File(RedisLimitRateWithLUA.class.getResource("/").toURI().getPath() + "limit.lua"); 
  4.  String luaScript = FileUtils.readFileToString(luaFile); 
  5.  String key = "ip:" + System.currentTimeMillis()/1000; // 当前秒 
  6.  String limit = "5"; // 最大限制 
  7.  List<String> keys = new ArrayList<String>(); 
  8.  keys.add(key); 
  9.  List<String> args = new ArrayList<String>(); 
  10.  args.add(limit); 
  11.  Long result = (Long)(jedis.eval(luaScript, keys, args)); // 执行lua脚本,传入参数 
  12.  return result == 1; 

解释:

  • Java代码传入key和最大的限制limit参数进lua脚本
  • 执行lua脚本(lua脚本判断当前key是否超过了最大限制limit)
  • 如果超过,则返回0(限流)
  • 如果没超过,返回1(程序继续执行)

【编辑推荐】

  1. MySQL中常见的字符串函数应用详解
  2. 面向对象之三个基本特征(JavaScript)
  3. 面向对象之七大基本原则(JavaScript)
  4. Java编程语言环境OpenJDK 13发布:龙芯贡献全球前5
  5. 忘掉Java并发,先听完这个故事...
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

用Python玩转excel

用Python玩转excel

让重复操作傻瓜化
共3章 | DE8UG

151人订阅学习

AI入门级算法

AI入门级算法

算法常识
共22章 | 周萝卜123

127人订阅学习

这就是5G

这就是5G

5G那些事儿
共15章 | armmay

122人订阅学习

读 书 +更多

Linux服务器安全策略详解

Linux主要用于架设网络服务器。如今关于服务器和网站被黑客攻击的报告几乎每天都可以见到,而且随着网络应用的丰富多样,攻击的形式和方法...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微