SpringBoot Redis分布式鎖Lua腳本釋放報錯:如何解決返回值類型不匹配和IllegalStateException異常?

SpringBoot Redis分布式鎖Lua腳本釋放報錯:如何解決返回值類型不匹配和IllegalStateException異常?

springBoot redis分布式lua腳本釋放異常分析及解決方案

在使用SpringBoot集成redis實現分布式鎖時,運用Lua腳本進行鎖釋放可能會遇到返回值類型不匹配和IllegalStateException異常。本文將通過一個案例分析問題根源并提供解決方案。

問題描述:

開發者使用Lua腳本釋放Redis分布式鎖,代碼片段如下:

public void unlock(String key, Object value) {     String script = "if (redis.call('get',KEYS[1]) == ARGV[1]) then return redis.call('del',KEYS[1]) else return 0 end";     DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script);     Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), value); }

該腳本旨在檢查key的值是否與傳入的value匹配,若匹配則刪除key(釋放鎖),否則返回0。運行時出現以下問題:

  • 問題一:redisTemplate.execute()返回值類型為Object,而非預期的Long。
  • 問題二:單元測試執行unlock方法時拋出org.springframework.data.redis.RedisSystemException: Redis exception; nested exception is io.lettuce.core.RedisException: Java.lang.IllegalStateException異常。

錯誤原因及解決方案:

分析錯誤日志和代碼,問題主要在于兩點:

  1. Collections.singletonList(key)類型不匹配:雖然Collections.singletonList(key)返回List,但redisTemplate在處理keys參數時存在類型轉換問題。 Lua腳本期望KEYS為一個鍵的數組。 使用ArrayList明確指定keys參數類型可以解決此問題。

  2. redisTemplate泛型類型不匹配:DefaultRedisScript期望返回Long類型,但redisTemplate可能無法直接返回Long類型。Lua腳本返回的是數值型字符串,需要進行類型轉換。使用StringRedisTemplate更適合處理字符串類型的key和value,并能更直接地處理Lua腳本返回的數值型字符串。

修改后的代碼:

StringRedisTemplate stringRedisTemplate; // Inject StringRedisTemplate  public void unlock(String key, String value) { // Changed Object value to String value     String script = "if (redis.call('GET',KEYS[1]) == ARGV[1]) then return redis.call('DEL',KEYS[1]) else return 0 end";     DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script);     redisScript.setResultType(Long.class); // Explicitly set the result type     List<String> keys = new ArrayList<>();     keys.add(key);     Long result = stringRedisTemplate.execute(redisScript, keys, value); // Use StringRedisTemplate     System.out.println(result); }

通過以上修改,解決了返回值類型不匹配和IllegalStateException異常,確保Lua腳本正確執行。 關鍵在于使用StringRedisTemplate并顯式設置redisScript的resultType為Long.class。 同時,將value參數的類型改為String,以匹配Lua腳本中的ARGV[1]。

記住在你的spring boot配置中注入StringRedisTemplate。 例如:

@Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {     StringRedisTemplate template = new StringRedisTemplate();     template.setConnectionFactory(redisConnectionFactory);     return template; }

? 版權聲明
THE END
喜歡就支持一下吧
點贊11 分享