一、简介

1)分布式限流

如果是单实例项目,我们使用Guava这样的轻便又高性能的堆缓存来处理限流。但是当项目发展为多实例了以后呢?这时候我们就需要采用分布式限流的方式,分布式限流可以以redis + lua 或者 nignx + lua这样的组合来实现。。

分布式限流一般应用场景都是在业务上进行限流,所以本文不涉及niginx + lua,简单介绍redis + lua分布式限流的实现。如果是需要在接入层限流的话,应该直接采用nginx自带的连接数限流模块和请求限流模块。

2)redis

redis是一种键值对的单线程架构模型,所以它是线程安全的,也是分布式缓存常用的解决方案。(本文不涉及redis的分布式缓存,只是讲如何结合redis实现限流)

3)lua

lua是基于c语言的一种脚本语言,它可以很轻便地被使用在嵌入式方面。我们不会去重写redis,但是我们可以去使用lua来扩展redis的功能。而redis也内置了对lua支持的模块。

二、示例

示例图

1)启动redis服务

我们得确保redis安装,并使用: ./redis-server 命令启动redis服务端

redis常用命令:

./redis-server 启动服务端
./redis-cli 启动客户端
./redis-cli shutdown 关闭服务
keys * 查看所有key
get 键 根据键获取值

2)编写lua脚本

创建limit.lua文件

local key = KEYS[] --限流KEY(一秒一个)
local limit = tonumber(ARGV[]) --限流大小
local current = tonumber(redis.call('get', key) or "0")
if current + > limit then --如果超出限流大小
return
else --请求数+1,并设置2秒过期
redis.call("INCRBY", key,"1")
redis.call("expire", key,"1")
return
end

1)我们通过KEYS[1] 获取传入的key参数

2)通过ARGV[1]获取传入的limit参数

3)redis.call方法,从缓存中get和key相关的值,如果为nil那么就返回0

4)接着判断缓存中记录的数值是否会大于限制大小,如果超出表示该被限流,返回0

5)如果未超过,那么该key的缓存值+1,并设置过期时间为1秒钟以后,并返回1

3)Java调用

java采用jedis来操作redis

引入redis依赖和apache io的工具包(方便读取lua文件的内容)

 <dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency> <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>

创建RedisLua.java文件

import org.apache.commons.io.FileUtils;
import redis.clients.jedis.Jedis; import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch; /**
* @author lay
* @date 2018/5/22.
* @time 17:48
*/
public class RedisLua { public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(1); for (int i = 0; i < 7; i++) {
new Thread(new Runnable() {
public void run() {
try {
latch.await();
System.out.println(accquire());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); } latch.countDown();
} public static boolean accquire() throws IOException, URISyntaxException {
Jedis jedis = new Jedis("127.0.0.1");
File luaFile = new File(RedisLua.class.getResource("/").toURI().getPath() + "limit.lua");
String luaScript = FileUtils.readFileToString(luaFile); String key = "ip:" + System.currentTimeMillis()/1000; // 当前秒
String limit = "5"; // 最大限制
List<String> keys = new ArrayList<String>();
keys.add(key);
List<String> args = new ArrayList<String>();
args.add(limit);
Long result = (Long)(jedis.eval(luaScript, keys, args)); // 执行lua脚本,传入参数
return result == 1;
}
}

以上模拟了多线程下,使用jedis连接redis服务,并执行lua脚本

4)输出结果

true
true
false
true
false
true
true

我们看到,7个线程,其中2个线程被判断为false

参考文章:http://jinnianshilongnian.iteye.com/blog/2305117

最新文章

  1. Javascript实现HashTable类
  2. 使用Redis的INCR、Hsetnx、Hincrby的命令生成序列号
  3. Android 在Windows上安装FFmpeg程序
  4. 如何让 Drupal 使用 Wordpress 形式的编辑代码?
  5. Spark大型项目实战:电商用户行为分析大数据平台
  6. NSIS:判断程序是否运行并进行卸载
  7. omi-cli新版发布-升级webpack2和支持sass生成组件局部CSS
  8. MongoDB $type条件操作符
  9. [ExtJS5学习笔记]第十七节 Extjs5的panel组件增加accodion成为折叠导航栏
  10. css 优化
  11. Python框架学习之Flask中的Jinja2模板
  12. 洛谷 P2119 魔法阵
  13. 剑指offer(53)表示数值的字符串
  14. ORM作业
  15. linux eclipse 报错过时的方法
  16. python -- 判断给定的参数是否是地理位置的经度和纬度
  17. java-http通信调用与创建
  18. 【详解】JNI (Java Native Interface) (三)
  19. xtrabackup 源码安装
  20. 兼容安卓和ios实现一键复制内容到剪切板

热门文章

  1. kvm虚拟化之kvm虚拟机控制台登陆
  2. [Objective-C语言教程]循环语句(9)
  3. 提高 iOS App 通知功能启用率的三个策略
  4. h5 fieldset
  5. js数组的常用操作
  6. Ubuntu16.04+Cuda8.0+cuDNN6配置py-faster rcnn(转)
  7. my.等级限制
  8. http-https php文件下载
  9. Dubbo---初识
  10. Caused by java.lang.IllegalStateException Not allowed to start service Intent { cmp=com.x.x.x/.x.x.xService }: app is in background uid UidRecord问题原因分析(二)