在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
一、为什么使用Lua脚本 为了一次通信执行多个Redis命令,我们可以用pipline ,但是多个命令间没有逻辑联系 。
Lua脚本可以一次通信执行多个Redis命令,而且内部可以写自己的逻辑,整个脚本执行是原子性的。
二、命令行调用Lua脚本
EVAL script numkeys key [key ...] arg [arg ...] redis 127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 三、Lua脚本文件执行 1)保存脚本文件为ip_control.lua,用作IP控制的脚本
local times = redis.call('incr',KEYS[1]) if times == 1 then redis.call('expire',KEYS[1], ARGV[1]) end if times > tonumber(ARGV[2]) then return 0 end return 1 2)redis客户端执行脚本 多次执行此脚本则会触发限流,返回值为0 redis-cli -h localhost -p 6381 --eval /root/ip_control.lua rate_limit:127.0.0.1 , 60 3 -h 服务器IP -p 服务器端口 执行脚本: /root/ip_control.lua 保存的Key: rate_limit:127.0.0.1 数据存放时间60s则超时 , 限流的数量为3 四、Java调用Lua脚本进行数据量控制-我用的是spring中的 RedisTemplate,所以里面引入了spring相关的包 1)maven中的jar引入 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>test111</artifactId> <version>1.0-SNAPSHOT</version> <name>test111</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.2.2</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.0.1-jre</version> </dependency> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.10.5</version> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.3</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.7</version> </dependency> </dependencies> <profiles> <profile> <id>dev</id> <properties> <profiles.active>dev</profiles.active><!--开发环境 --> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>test</id> <properties> <profiles.active>test</profiles.active><!--测试环境 --> </properties> </profile> <profile> <id>prod</id> <properties> <profiles.active>prod</profiles.active><!--生产环境 --> </properties> </profile> </profiles> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>test.Server</mainClass> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> <encoding>${project.build.sourceEncoding}</encoding> <showWarnings>true</showWarnings> <showDeprecation>true</showDeprecation> </configuration> </plugin> </plugins> <resources> <resource> <directory>src/main/properties</directory> <filtering>true</filtering> <excludes> <exclude>application.properties</exclude> <!-- <exclude>application-dev.properties</exclude> <exclude>application-test.properties</exclude> <exclude>application-product.properties</exclude>--> </excludes> </resource> <resource> <directory>src/main/properties</directory> <filtering>true</filtering> <includes> <include>application.properties</include> <include>ip_control.lua</include> <include>application-${profiles.active}.properties</include> </includes> </resource> </resources> </build> </project> 2)spring的配置文件-application.properties
这里是哨兵模式引入的
####redis的配置信息### spring.redis.sentinel.master=mymaster spring.redis.sentinel.nodes=192.168.112.131:26379,192.168.112.131:26380,192.168.112.131:26381 spring.redis.password= #采用哪个数据库 spring.redis.database=0 # 连接池最大连接数,默认8个,(使用负值表示没有限制) spring.redis.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 连接池中的最大空闲连接 spring.redis.pool.max-idle=8 # 连接池中的最小空闲连接 spring.redis.pool.min-idle=0 # 连接超时时间(毫秒) spring.redis.timeout=0 3)使用redisTemplate调用脚本 @SpringBootTest @RunWith(SpringRunner.class) public class RedisTest { @Resource private RedisTemplate redisTemplate; @Test public void test2(){ String luaIpControl = null; try { //文件路径也可以使用相对路径 luaIpControl = FileUtils.readFileToString(new File("E:\\github\\test2\\target\\classes\\ip_control.lua"),"utf-8"); //RedisTest.class.getClassLoader().getResourceAsStream("ip_control.lua"); } catch (IOException e) { e.printStackTrace(); } System.out.println( luaIpControl ); String key = "rate_limit:127.0.0.1"; Integer expire = 30; Integer maxVisit = 10 ; Long result = null; for (int i = 0; i < 11; i++) { result = invokScript( key , expire ,maxVisit , luaIpControl); } System.out.println( result ); } public Long invokScript(String key, int expire ,int maxVisit, String script) { // 脚本里的KEYS参数 List<String> keys = new ArrayList<>(); keys.add(key); // 脚本里的ARGV参数 List<String> args = new ArrayList<>(); args.add(Integer.toString(expire)); args.add(Integer.toString(maxVisit)); Long result = (long) redisTemplate.execute(new RedisCallback<Long>() { @Override public Long doInRedis(RedisConnection connection) throws DataAccessException { Object nativeConnection = connection.getNativeConnection(); // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行 // 集群模式 if (nativeConnection instanceof JedisCluster) { return (Long) ((JedisCluster) nativeConnection).eval(script, keys, args); } // 单机模式 else if (nativeConnection instanceof Jedis) { return (Long) ((Jedis) nativeConnection).eval(script, keys, args); } /*else if (nativeConnection instanceof Redisson) { Redisson redisson = (Redisson)nativeConnection; return redisson.getScript().eval(RScript.Mode.READ_WRITE,STOCK_LUA,RScript.ReturnType.INTEGER, Collections.singletonList(keys), new List[]{args}); }*/ return null; } }); return result; } @Test public void test1(){ redisTemplate.opsForValue().set("name","wang" ,100 ,TimeUnit.SECONDS); String str = (String) redisTemplate.opsForValue().get("name"); System.out.println( str ); } }
|
请发表评论