Java+redis+lua 实现在用lua脚本执行Redis相关的操作

1.将lua脚本的内容加载出来放入到DefaultRedisScript
复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;

@Configuration
public class LuaConfig {
    /**
     * 将lua脚本的内容加载出来放入到DefaultRedisScript     *
     * @return
     */
  @Bean
  public DefaultRedisScript<String> getLua() {
      DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<>();
      defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/get.lua")));
      defaultRedisScript.setResultType(String.class);
      return defaultRedisScript;
  }
}
复制代码

2. RedisManager 操作类

复制代码
/**
 * RedisManager 操作类
 * 使用SpringDataRedis进行整合
 *
 * @author yy
 * @since 1.0.0, 2019年08月20日
 */
@Component
public class RedisManager {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
public Object lua(RedisScript redisScript, List<String> keys, Long defaultExpire) {

        RedisSerializer<String> stringRedisSerializer = stringRedisTemplate.getStringSerializer();
        Object result = stringRedisTemplate.execute(redisScript, stringRedisSerializer, stringRedisSerializer, keys, String.valueOf(defaultExpire));

        System.out.println(result);
        return result;
    }

    /**
     * 执行 lua 脚本
     * @author hengyumo
     * @since 2021-06-05
     *
     * @param luaScript  lua 脚本
     * @param returnType 返回的结构类型
     * @param keys       KEYS
     * @param argv       ARGV
     * @param <T>        泛型
     *
     * @return 执行的结果
     */
    public <T> Object executeLuaScript(String luaScript, Class<T> returnType, String[] keys, String... argv) {
        return  stringRedisTemplate.execute(RedisScript.of(luaScript, returnType),
                new StringRedisSerializer(),
                new GenericToStringSerializer<>(returnType),
                Arrays.asList(keys),
                (Object[])argv);
    }

    /**
     * @author hengyumo
     * @since 2021-06-05
     *
     * 删除以key为前缀的所有键值
     * @param keyPre 前缀
     * @return  返回删除掉的所有key
     */
    public List<String> deleteKeysWithPre(String luaScript,String keyPre) {
        @SuppressWarnings("unchecked")
        List<Object> result = (List<Object>) executeLuaScript(luaScript, List.class, new String[] {keyPre});
        return result.stream().map(x -> {
            if (x instanceof List) {
                @SuppressWarnings("unchecked")
                List<String> list = (List<String>) x;
                if (list.size() > 0) {
                    return list.get(0);
                }
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 执行lua 脚本
     * @param defaultRedisScript
     * @param keyList key 值集合
     * @return
     */
    public Object execute(DefaultRedisScript<String> defaultRedisScript, List<String> keyList) {
        return stringRedisTemplate.execute(defaultRedisScript, keyList);
    }

    /**
     * 执行lua 脚本
     * @param longDefaultRedisScript
     * @param asList  key 值集合
     * @param argv   vlue 的判断条件
     * @param <T>
     * @return
     */
    public <T> Object executeLong(DefaultRedisScript<Long> longDefaultRedisScript, List<String> asList,   Object[] argv) {
        return   stringRedisTemplate.execute(longDefaultRedisScript, asList, argv);
    }
    /**
     * 执行lua 脚本
     * @param longDefaultRedisScript
     * @param asList  key 值集合
     * @param argv   vlue 的判断条件
     * @param <T>
     * @return
     */
    public <T> Object execute(DefaultRedisScript<String> longDefaultRedisScript, List<String> asList,   Object[] argv) {
        return   stringRedisTemplate.execute(longDefaultRedisScript, asList, argv);
    }

    /**
     * 执行lua 脚本
     * @param longDefaultRedisScript
     * @param asList  key 值集合
     * @param value1  vlue 的第一个参数判断条件
     * @param value2  vlue 的第二个参数判断条件
     * @param <T>
     * @return
     */
    public <T> Object execute(DefaultRedisScript<Long> longDefaultRedisScript, List<String> asList, String value1, String value2) {
      return   stringRedisTemplate.execute(longDefaultRedisScript, asList, value1,value2);
    }
}
复制代码

 

3.测试lua执行redis的语句

 

复制代码
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisTestControllerTest {
    @Autowired
    private RedisManager redisTemplate;
    /**
     * 获取注入的lua脚本文件
     */
    @Autowired
    private DefaultRedisScript<String> getLua;

    @Test
    public void testExecuteLuaScript() {
        String script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}";
        List<Object> list = (List<Object>) redisTemplate.executeLuaScript(script,
                List.class, new String[]{"a", "b"}, "a", "b");
        list.forEach(x -> System.out.println(x.toString()));

        String script2 = "for i=1,KEYS[1],1 do local k=KEYS[2]..i; redis.call('set',k,ARGV[1]);" +
                "if ARGV[2] then redis.call('expire',k,ARGV[2]) end;end;" +
                "return redis.call('keys',KEYS[2]..'*');";
        List<Object> list2 = (List<Object>) redisTemplate.executeLuaScript(script2,
                List.class, new String[]{"10", "test"}, "0", "60");
        list2.forEach(x -> System.out.println(x.toString()));
    }

    @Test
    public void redisLuasetnx() {
        String script1 = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('set', KEYS[2], ARGV[2]) return 1 else return 0 end";
        Long result1 = (Long) redisTemplate.execute(
                new DefaultRedisScript<Long>(script1, Long.class),
                Arrays.asList("key3", "key4"),
                "value1", "value2"
        );
        System.out.println(result1); // 1
    }

    @Test
    public void redisLuaget() {
        //   如果key1==value1,则删除key1,返回删除的状态,否则返回0
        String script2 = "if redis.call('get', KEYS[1]) == tonumber(ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end";
        Long result2 = (Long) redisTemplate.executeLong(
                new DefaultRedisScript<Long>(script2, Long.class),
                Arrays.asList("key3"),
                new Object[]{"2"}
        );
        System.out.println(result2); // 1
    }
    /**
     * redis + lua + set 存数据
     */
    @Test
    public void redisLuaset() {
        String script1 = "return redis.call('set', KEYS[1],'bar')";
        String result1 = (String) redisTemplate.execute(
                new DefaultRedisScript<String>(script1, String.class),
                Arrays.asList("key_bar")
        );
        System.out.println(result1); // 1
    }
    /**
     * redis + lua + set 存数据
     */
    @Test
    public void redisLuasetvalue() {
        String script1 = "return redis.call('set', KEYS[1],ARGV[1])";
        String result1 = (String) redisTemplate.execute(
                new DefaultRedisScript<String>(script1, String.class),
                Arrays.asList("key_bar2"),
                new Object[]{"2"}
        );
        System.out.println(result1); // 1
    }

    /**
     * redis + lua + get 取数据
     */
    @Test
    public void redisLuaGet1() {

        String script1 = "return redis.call('get', KEYS[1])";
        Object result1 = redisTemplate.execute(
                new DefaultRedisScript<>(script1, String.class),
                Collections.singletonList("key_bar")
        );
        System.out.println(result1); // 1
    }
    /**
     * redis + lua + set 存数据 加过期时间
     */
    @Test
    public void redisLuaSetTime() {
     //   String script0 = "return redis.call('set', KEYS[1],'bar', 'nx', 'ex', 10)";
        String script1 = "return redis.call('set', KEYS[1],'bar', 'nx', 'ex', ARGV[1])";
        Long result1 = (Long) redisTemplate.executeLong(
                new DefaultRedisScript<>(script1, Long.class),
                Collections.singletonList("key_time1"),
                new Object[]{"130"}
        );
        System.out.println(result1); // 1
    }

    /**
     * redis + lua + set 存数据 加过期时间
     */
    @Test
    public void redisLuaSetTime2() {
        //   String script0 = "return redis.call('set', KEYS[1],'bar', 'nx', 'ex', 10)";
        String script1 = "return redis.call('set', KEYS[1],ARGV[1], 'nx', 'ex', ARGV[2])";
        String result1 = (String) redisTemplate.execute(
                new DefaultRedisScript<>(script1, String.class),
                Collections.singletonList("key_time1"),
                new Object[]{"130","60"}
        );
        System.out.println(result1); // 1
    }


    /**
     * 以下命令删除xxx*格式的所有key值
     */
    @Test
    public void testDeleteKeysWithPre() {
        // 以下命令删除xxx*格式的所有key值
        String luaScript =
                "local redisKeys = redis.call('keys',KEYS[1]..'*');" +
                        "for i,k in pairs(redisKeys) do redis.call('del',k);end;" +
                        "return redisKeys;";

        List<String> list = redisTemplate.deleteKeysWithPre(luaScript, "DAWN");
        list.forEach(System.out::println);
    }

    /**
     * 测试并发,多人同时在一时间取数据
     * @throws Exception
     */
    @Test
    public void lpushbuy1() throws Exception {
        // 请求总数
        int clientTotal = 5000;
        // 同时并发执行的线程数
        int threadTotal = 200;
        //  Random rd=new Random();
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    Long userId = Math.round(Math.random() * 100000000);
                    Long goodsId = 1L;
                    lpushbuy(goodsId, userId);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                semaphore.release();
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
    }

    /**
     * 秒杀功能
     * @param goodsId
     * @param userId
     */
    public void lpushbuy(Long goodsId, Long userId) {
        String script0 = "  local u = redis.call ('get', KEYS[1]);if u then   return 2;  else   local v = redis.call ('lpop', KEYS[2]);if v then  redis.call ('set', KEYS[1], ARGV[1]); return 1 else return 0; end;  end;  ";
        String script1 = "  local u = redis.call ('get', KEYS[1]);if u then   return 2;  else   local v = redis.call ('lpop', KEYS[2]);if v then  redis.call ('set', KEYS[1], ARGV[1], 'nx', 'ex', ARGV[2]); return 1 else return 0; end;  end;  ";

        Long result1 = (Long) redisTemplate.execute(
                new DefaultRedisScript<>(script1, String.class),
                Arrays.asList("user_x:" + userId, "goodsId_x:" + goodsId),
                new Object[]{String.valueOf(goodsId), "100"});
        System.out.println("result1!!!!!!!!!!!!!!!!!!!!!! =========" + result1);
        if (result1 == 0) {
            System.out.println("结束");
        } else if (result1 == 1) {
            System.out.println("成功");
        } else if (result1 == 2) {
            System.out.println("已经获取到了不逼重复获取");
        } else {
            System.out.println("失败");
        }
    }

    @Test
    public void lpushbuy2() {
        Long userId = Math.round(Math.random() * 100000000);
        Long goodsId = 1L;
        lpushbuy(goodsId, userId);
    }

    /**
     * 测试lua脚本写在文件里面
     */
    @Test
    public void getlua() {
        String redisKey = "aaa";
        Object result1 = redisTemplate.execute(
                getLua,
                Collections.singletonList(redisKey));
        System.out.println(result1.toString());
    }


}
复制代码

4.lua文件

在项目的resources 文件下面建立一个get.lua文件,里面写入一下内容

return redis.call('get', KEYS[1])

完成

 

posted @   黄橙  阅读(1901)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示