赞
踩
目录
Lua语言是在1993年由巴西一个大学研究小组发明,其设计目标是作为嵌入式程序移植到其他应用程序,它是由C语言实现的,虽然简单小巧但是功能强大,所以许多应用都选用它作为脚本语言,尤其是在游戏领域,暴雪公司的“魔兽世界”,“愤怒的小鸟”,Nginx将Lua语言作为扩展。Redis将Lua作为脚本语言可帮助开发者定制自己的Redis命令。
Redis 2.6 版本通过内嵌支持 Lua 环境。也就是说一般的运用,是不需要单独安装Lua的。
通过使用LUA脚本:
1、减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行;
2、原子操作,redis会将整个脚本作为一个整体执行,中间不会被其他命令插入(Redis执行命令是单线程)。
3、复用性,客户端发送的脚本会永远存储在redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑。
不过为了我们方便学习Lua语言,我们还是单独安装一个Lua。
在Redis使用LUA脚本的好处包括:
1、减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行;
2、原子操作,Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话说,编写脚本的过程中无需担心会出现竞态条件;
3、复用性,客户端发送的脚本会存储在Redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑
eval script numkeys key [key ...] arg [arg ...]
是一段 Lua 脚本程序,它会被运行在Redis 服务器上下文中,这段脚本不必(也不应该)定义为一个 Lua 函数。
用于指定键名参数的个数。
从 eval 的第三个参数开始算起,使用了 numkeys 个键(key),表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用1为基址的形式访问(KEYS[1],KEYS[2]···)。
可以在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似(ARGV[1],ARGV[2]···)。
- # “2”是key的数量
- eval "return redis.call('mset',KEYS[1],ARGV[1],KEYS[2],ARGV[2])" 2 goudan wangcai 111 222
- -- java端送入三个参数(1个key,2个param)string
- -- limitKey(redi中key的值)
- local key = KEYS[1];
-
- -- nums(次数)
- local nums = ARGV[1];
-
- -- expire(秒S)
- local expire = ARGV[2];
-
- -- 对key-value中的 value +1的操作 返回一个结果
- local afterval = redis.call('incr', key);
-
- -- 第一次
- if afterval == 1 then
-
- -- 失效时间(1S) TLL 1S
- redis.call('expire', key, tonumber(expire))
-
- -- 第一次不会进行限制
- return 1;
- end
-
- -- 不是第一次,进行判断
- if afterval > tonumber(nums) then
-
- -- 限制了
- return 0;
- end
- return 1;
- @RestController
- public class Controller {
-
- @Autowired
- private IsAcquire isAcquire;
-
- //秒杀接口
- @RequestMapping("/order")
- public String killProduct(@RequestParam(required = true) String name) throws Exception {
-
- // 调用 isAcquire 来判断是否被限流,当前key为“iphone”,60秒只能进行10次
- // 如果返回true,则表示没有到阈值
- // 返回false,则表示60秒内超过10次
- if (isAcquire.acquire("iphone", 10, 60)) {
-
- System.out.println("业务成功!");
- return "恭喜(" + name + "),抢到iphone!";
- } else {
-
- System.out.println("-----------业务被限流");
- return "对不起,你被限流了!";
- }
- }
- }
-
- // 使用Lua脚本实现限流(为了方便查看,都写在一个代码块中)
- @Service
- public class IsAcquire {
-
- //引入一个Redis的Lua脚本的支持
- private DefaultRedisScript<Long> getRedisScript;
-
- //判断限流方法
- public boolean acquire(String Key, int nums, int expire) throws Exception {
- //连接Redis
- Jedis jedis = new Jedis("192.168.175.4", 6379);
- getRedisScript = new DefaultRedisScript<>();
-
- //脚本执行返回值 long
- getRedisScript.setResultType(Long.class);
-
- // 读取到当前Lua脚本
- getRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));
-
- // 执行Lua脚本
- Long result = (Long) jedis.eval(getRedisScript.getScriptAsString(),
- 1, Key, String.valueOf(nums), String.valueOf(expire));
-
- // 返回0时表示被限流,返回不为0则表示正常
- if (result == 0) {
- return false;
- }
- return true;
- }
- }
测试路径:http://127.0.0.1:8080/order?name=aiqingyu
策略:60秒只能进行10次
方案好处:支持分布式
使用lua脚本的好处:减少网络开销、原子操作、复用
简单粗暴,但是有临界问题
在线演示滑动窗口:
- 滑动窗口通俗来讲就是一种流量控制技术。它本质上是描述接收方的TCP数据报缓冲区大小的数据,发送方根据这个数据来计算自己最多能发送多长的数据,如果发送方收到接收方的窗口大小为0的TCP数据报,那么发送方将停止发送数据,等到接收方发送窗口大小不为0的数据报的到来。
- 首先是第一次发送数据这个时候的窗口大小是根据链路带宽的大小来决定的。我们假设这个时候窗口的大小是3。这个时候接受方收到数据以后会对数据进行确认告诉发送方我下次希望手到的是数据是多少。这里我们看到接收方发送的ACK=3(这是发送方发送序列2的回答确认,下一次接收方期望接收到的是3序列信号)。这个时候发送方收到这个数据以后就知道我第一次发送的3个数据对方只收到了2个。就知道第3个数据对方没有收到。下次在发送的时候就从第3个数据开始发。
- 此时窗口大小变成了2 ,于是发送方发送2个数据。看到接收方发送的ACK是5就表示他下一次希望收到的数据是5,发送方就知道我刚才发送的2个数据对方收了这个时候开始发送第5个数据。
- 这就是滑动窗口的工作机制,当链路变好了或者变差了这个窗口还会发生变话,并不是第一次协商好了以后就永远不变了。
- 所以滑动窗口协议,是TCP使用的一种流量控制方法。该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。
- 只有在接收窗口向前滑动时(与此同时也发送了确认),发送窗口才有可能向前滑动。
- 收发两端的窗口按照以上规律不断地向前滑动,因此这种协议又称为滑动窗口协议。
定义
- 先有一个桶,桶的容量是固定的。
- 以任意速率向桶流入水滴,如果桶满了则溢出(被丢弃)。
- 桶底下有个洞,按照固定的速率从桶中流出水滴。
特点
漏桶核心是:请求来了以后,直接进桶,然后桶根据自己的漏洞大小慢慢往外面漏。
具体实现的时候要考虑性能(比如Redis实现的时候数据结构的操作是不是会导致性能问题)
定义
- 先有一个桶,容量是固定的,是用来放令牌的。
- 以固定速率向桶放令牌,如果桶满了就不放令牌了。
- 处理请求是先从桶拿令牌,先拿到令牌再处理请求,拿不到令牌同样也被限流了。
特点
突发情况下可以一次拿多个令牌进行处理。
具体实现的时候要考虑性能(比如Redis实现的时候数据结构的操作是不是会导致性能问题)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。