限流(三)Redis + lua分布式限流
一、简介
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
限流(三)Redis + lua分布式限流的更多相关文章
- springboot + aop + Lua分布式限流的最佳实践
整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 一.什么是限流?为什么要限流? 不知道大家有没有做过帝都的地铁, ...
- Redis+Lua实现限流
相比Redis事务来说,Lua脚本有以下优点减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;原子操作: Redis 将整个脚本作为一个原子 ...
- SpringBoot--使用redis实现分布式限流
1.引入依赖 <!-- 默认就内嵌了Tomcat 容器,如需要更换容器也极其简单--> <dependency> <groupId>org.springframew ...
- spring boot:用redis+lua实现基于ip地址的分布式流量限制(限流/简单计数器算法)(spring boot 2.2.0)
一,限流有哪些环节? 1,为什么要限流? 目的:通过对并发请求进行限速或者一个时间单位内的的请求进行限速,目的是保护系统可正常提供服务,避免被压力太大无法响应服务. 如果达到限制速率则可以采取预定的处 ...
- 分布式限流组件-基于Redis的注解支持的Ratelimiter
原文:https://juejin.im/entry/5bd491c85188255ac2629bef?utm_source=coffeephp.com 在分布式领域,我们难免会遇到并发量突增,对后端 ...
- Redis实现的分布式锁和分布式限流
随着现在分布式越来越普遍,分布式锁也十分常用,我的上一篇文章解释了使用zookeeper实现分布式锁(传送门),本次咱们说一下如何用Redis实现分布式锁和分布限流. Redis有个事务锁,就是如下的 ...
- 【分布式架构】--- 基于Redis组件的特性,实现一个分布式限流
分布式---基于Redis进行接口IP限流 场景 为了防止我们的接口被人恶意访问,比如有人通过JMeter工具频繁访问我们的接口,导致接口响应变慢甚至崩溃,所以我们需要对一些特定的接口进行IP限流,即 ...
- Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流
1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...
- 基于kubernetes的分布式限流
做为一个数据上报系统,随着接入量越来越大,由于 API 接口无法控制调用方的行为,因此当遇到瞬时请求量激增时,会导致接口占用过多服务器资源,使得其他请求响应速度降低或是超时,更有甚者可能导致服务器宕机 ...
随机推荐
- “全栈2019”Java第十四章:二进制、八进制、十六进制
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- scrapy连接MySQL
Scrapy中连接MySQL所需要做的工作如下: 1.settings中需要设置的部分 # 启动管道组件 ITEM_PIPELINES = { 'QianChengWuYu.mongoDBPiplel ...
- selenium上传图片
在我们使用selenium的时候碰到上传图片.文件时一般都可以先定位然后直接send_keys,但是有的却不行,selenium也没有提供其它的办法,只能靠第三方软件来解决 我们要借助一个叫AutoI ...
- 图论 竞赛图(tournament)学习笔记
竞赛图(tournament)学习笔记 现在只是知道几个简单的性质... 竞赛图也叫有向完全图. 其实就是无向完全图的边有了方向. 有一个很有趣的性质就是:一个tournament要么没有环,如果 ...
- leetcode-806-Number of Lines To Write String
题目描述: We are to write the letters of a given string S, from left to right into lines. Each line has ...
- RPC与本地调用的区别
RPC远程调用:一般可以跨平台通讯,采用http协议.因为http协议底层使用socket技术,只要你的语言支持socket技术,就可以相互进行通讯.比如:java语言开发的接口,http协议,然后让 ...
- session和cookie的知识总结
1.HTTP协议 由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP连接.HTTP服务器则在那个端口监听客户端发送过来的请求.一旦收到请求,服务器(向客户端)发回一个状态 ...
- [转] vagrant学习笔记 - provision
[From] https://blog.csdn.net/54powerman/article/details/50684844 从字面上来看,provision是准备,实现的功能是在原生镜像的基础 ...
- [Java基础]-- Java GC 垃圾回收器的分类和优缺点
https://blog.csdn.net/high2011/article/details/80177473?utm_source=blogxgwz2 参考:elasticsearch实战-使用G1 ...
- php的魔术常量以及类的模式方法
魔术方法class A { const PI = 3.14; static $type = 'type1'; public $a1='a1'; public function fun1(){ var_ ...