基于Redis+Lua脚本实现分布式限流组件封装的方法_Redis

来源:脚本之家  责任编辑:小易  

数据完整性从很多方面来看,Redis 很像当初采用 InnoDB 前的 MySQL。而 Redis 采用了一种很合理的方式来保证数据完整性(复制62616964757a686964616fe58685e5aeb931333365663432,AOF 等),并且从 Redis2.6 开始引入的 Lua 脚本在功能与易用性方面为 Redis 的成长提供了很大助力。相对来说,Lua 脚本与其他数据库中的存储过程很相似,但脚本的执行有些许不同。在本文中最重要的一点就是一旦将脚本写入数据库,它会一直执行直到以下任一种情况出现:1. 完成所有工作,所有写操作处理完成后脚本会自动退出。2. 脚本运行时出错并中途退出,所有以前执行的写操作都已发生,但不会再有其他写操作。3. Redis 通过 SHUTDOWN NOSAVE 关闭时(不保存)。4. 你附加了调试器来“使”脚本完成 #1 与 #2 (或其他手段来保证不会丢失数据)。对于使用数据库开发软件的人,我想你也认同只有情景 #1 是最理想的。情景 #2,#3,#4 都会导致数据异常(#2 与 #4)和/或数据丢失(#3 和 #4)。如果你很重视数据,你应该尽可能地阻止数据异常与丢失。这不是哲学,而是工作(This is not philosophy, this is doing your job)。但很遗憾目前的 Redis 也帮不了你多少。所以我决定改变这种情况,数据2113完整性 面看Redis 像初采用 InnoDB 前 MySQL Redis 采用种合5261理式4102保证数据完整性(复制AOF 等)并且 Redis二1653.陆 始引入 Lua 脚本功能与内易容用性面 Redis 提供助力 相说Lua 脚本与其数据库存储程相似脚本执行些许同本文重要点旦脚本写入数据库直执行直任种情况现: 一. 完所工作所写操作处理完脚本自退 二. 脚本运行错并途退所前执行写操作都已发再其写操作 三. Redis 通 SHUTDOWN NOSAVE 关闭(保存) 四. 附加调试器使脚本完 #一 与 #二 (或其手段保证丢失数据) 于使用数据库发软件我想认同情景 #一 理想情景 #二#三#四 都导致数据异(#二 与 #四)/或数据丢失(#三 #四)重视数据应该尽能阻止数据异与丢失哲工作(This is not philosophy, this is doing your job)遗憾目前 Redis 帮少所我决定改变种情本回答被提问者采纳www.zgxue.com防采集请勿采集本网。

创建限流组件项目

主要用途是: (1)描述界面:WOW和剑网三的界面都是用LUA写的; (2)沟通引擎:游戏图形引擎提供了一些接口库,可以在LUA中调用; (3)服务器端:有些游戏,例如剑网三,在服务器端也会大量使用LUA。

pom.xml文件中引入相关依赖

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency> </dependencies>

在resources目录下创建lua脚本  ratelimiter.lua

---- Created by IntelliJ IDEA.-- User: 寒夜-- -- 获取方法签名特征local methodKey = KEYS[1]redis.log(redis.LOG_DEBUG, 'key is', methodKey) -- 调用脚本传入的限流大小local limit = tonumber(ARGV[1]) -- 获取当前流量大小local count = tonumber(redis.call('get', methodKey) or "0") -- 是否超出限流阈值if count + 1 > limit then -- 拒绝服务访问 return falseelse -- 没有超过阈值 -- 设置当前访问的数量+1 redis.call("INCRBY", methodKey, 1) -- 设置过期时间 redis.call("EXPIRE", methodKey, 1) -- 放行 return trueend

创建RedisConfiguration 类

package com.imooc.springcloud; import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript; /** * @author 寒夜 */@Configurationpublic class RedisConfiguration { @Bean public RedisTemplate<String, String> redisTemplate( RedisConnectionFactory factory) { return new StringRedisTemplate(factory); } @Bean public DefaultRedisScript loadRedisScript() { DefaultRedisScript redisScript = new DefaultRedisScript(); redisScript.setLocation(new ClassPathResource("ratelimiter.lua")); redisScript.setResultType(java.lang.Boolean.class); return redisScript; } }

创建一个自定义注解 

package com.hy.annotation; import java.lang.annotation.*; /** * @author 寒夜 */@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface AccessLimiter { int limit(); String methodKey() default ""; }

创建一个切入点

package com.hy.annotation; import com.google.common.collect.Lists;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.RedisScript;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils; import java.lang.reflect.Method;import java.util.Arrays;import java.util.stream.Collectors; /** * @author 寒夜 */@Slf4j@Aspect@Componentpublic class AccessLimiterAspect { private final StringRedisTemplate stringRedisTemplate; private final RedisScript<Boolean> rateLimitLua; public AccessLimiterAspect(StringRedisTemplate stringRedisTemplate, RedisScript<Boolean> rateLimitLua) { this.stringRedisTemplate = stringRedisTemplate; this.rateLimitLua = rateLimitLua; } @Pointcut(value = "@annotation(com.hy.annotation.AccessLimiter)") public void cut() { log.info("cut"); } @Before("cut()") public void before(JoinPoint joinPoint) { // 1. 获得方法签名,作为method Key MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); AccessLimiter annotation = method.getAnnotation(AccessLimiter.class); if (annotation == null) { return; } String key = annotation.methodKey(); int limit = annotation.limit(); // 如果没设置methodkey, 从调用方法签名生成自动一个key if (StringUtils.isEmpty(key)) { Class[] type = method.getParameterTypes(); key = method.getClass() + method.getName(); if (type != null) { String paramTypes = Arrays.stream(type) .map(Class::getName) .collect(Collectors.joining(",")); log.info("param types: " + paramTypes); key += "#" + paramTypes; } } // 2. 调用Redis boolean acquired = stringRedisTemplate.execute( rateLimitLua, // Lua script的真身 Lists.newArrayList(key), // Lua脚本中的Key列表 Integer.toString(limit) // Lua脚本Value列表 ); if (!acquired) { log.error("your access is blocked, key={}", key); throw new RuntimeException("Your access is blocked"); } } }

创建测试项目

pom.xml中引入组件

application.yml配置

spring: redis: host: 192.168.0.218 port: 6379 password: 123456 database: 0 application: name: ratelimiter-testserver: port: 10087

创建controller

package com.hy; import com.hy.annotation.AccessLimiter;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController; /** * @author 寒夜 */@RestController@Slf4jpublic class Controller { private final com.hy.AccessLimiter accessLimiter; public Controller(com.hy.AccessLimiter accessLimiter) { this.accessLimiter = accessLimiter; } @GetMapping("test") public String test() { accessLimiter.limitAccess("ratelimiter-test", 3); return "success"; } // 提醒! 注意配置扫包路径(com.hy路径不同) @GetMapping("test-annotation") @AccessLimiter(limit = 1) public String testAnnotation() { return "success"; } }

开始测试,快速点击结果如下

到此这篇关于基于Redis+Lua脚本实现分布式限流组件封装的方法的文章就介绍到这了,更多相关Redis+Lua脚本实现分布式限流组件内容请搜索真格学网以前的文章或继续浏览下面的相关文章希望大家以后多多支持真格学网! 您可能感兴趣的文章:SpringBoot通过RedisTemplate执行Lua脚本的方法步骤phpredis执行LUA脚本示例代码Redis执行Lua脚本的好处与示例代码redis中如何使用lua脚本让你的灵活性提高5个逼格详解Redis如何使用lua脚本实例教程Go语言中通过Lua脚本操作Redis的方法简介Lua脚本与Redis数据库的结合使用

主要用途是:2113 (1)描述界面:5261WOW和剑网4102三的界面都是用LUA写的;1653 (2)沟通版引擎:游戏图形权引擎提供了一些接口库,可以在LUA中调用; (3)服务器端:有些游戏,例如剑网三,在服务器端也会大量使用LUA,lua是一种嵌入式脚本语言,redis支持该语言 使开发运维人员可以写脚本对redis的数据进行分析、处理等操作。比如判断用户是否活跃、简单的DDOS防护、峰值平均值计算等~~具体看自己需要了内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • redis源码分析教程之压缩链表ziplist详解
  • 在redhat6.4安装redis集群【教程】
  • redis教程(四):hashes数据类型
  • redis快照模式_动力节点java学院整理
  • redis精确去重计数方法(咆哮位图)
  • 高效异步redis客户端aredis优劣势原理解析
  • redis学习之rdb、aof与复制时对过期键的处理教程
  • window手动操作清理redis缓存的技巧总结
  • linux redis 的安装步骤详解
  • redis教程(六):sorted-sets数据类型
  • redis lua脚本有什么用
  • 为什么在 Redis 实现 Lua 脚本事务
  • redis加载lua脚本,怎么获取数据
  • redis执行lua脚本的时候会不会删除过期键?
  • redis 集群支持lua脚本吗
  • 用lua 给redis 中塞值要怎么设置有效时间
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全mssqlmysqlmariadboracledb2mssql2008mssql2005sqlitepostgresqlmongodbredisaccess数据库文摘数据库其它首页springboot通过redistemplate执行lua脚本的方法步骤phpredis执行lua脚本示例代码redis执行lua脚本的好处与示例代码redis中如何使用lua脚本让你的灵活性提高5个逼格详解redis如何使用lua脚本实例教程go语言中通过lua脚本操作redis的方法简介lua脚本与redis数据库的结合使用redis源码分析教程之压缩链表ziplist详解在redhat6.4安装redis集群【教程】redis教程(四):hashes数据类型redis快照模式_动力节点java学院整理redis精确去重计数方法(咆哮位图)高效异步redis客户端aredis优劣势原理解析redis学习之rdb、aof与复制时对过期键的处理教程window手动操作清理redis缓存的技巧总结linux redis 的安装步骤详解redis教程(六):sorted-sets数据类型超强、超详细redis数据库入门教程redis常用命令、常见错误、配置技redis操作命令总结redis中5种数据结构的使用场景介64位windows下安装redis教程redis中使用redis-dump导出、导入redis中统计各种数据大小的方法redis常用命令小结让redis在你的系统中发挥更大作用centos 6.6下redis安装配置记录将音频文件转二进制分包存储到redis的实现redis3.2.6配置文件详细中文说明redis教程(十四):内存优化介绍redis中lru淘汰策略的深入分析redis与memcached的区别_动力节点java学院redis 实现队列原理的实例详解利用redis实现sql伸缩的方法centos linux系统下安装redis过程和配置参redis 5.05 单独模式安装及配置方法redis的主从同步解析
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved