• Redis调用Lua脚本并测试


    一、为什么使用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 );
        }
    }
  • 相关阅读:
    查看lwjgl常用状态的值
    微信公众号开发java框架:wx4j(MenuUtils篇)
    微信公众号开发java框架:wx4j(KefuUtils篇)
    微信公众号开发java框架:wx4j(MaterialUtils篇)
    微信公众号开发java框架:wx4j(入门篇)
    hashcode和equals方法小记
    https单向认证和双向认证区别
    java开发中获取路径的一些方式
    iOS使用sqlite3原生语法进行增删改查以及FMDB的使用
    IOS自动布局
  • 原文地址:https://www.cnblogs.com/lean-blog/p/14149569.html
Copyright © 2020-2023  润新知