redistemplate 使用lua脚本
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
注意事项
springboot使用RedisTemplate操作lua脚本的使用需要配置lettuce客户端,jedis是不支持的,现在springboot也默认是lettuce
spring:
redis:
cluster:
nodes: 192.168.101.98:2001,192.168.101.98:2002,192.168.101.98:2003,192.168.101.98:2004,192.168.101.98:2005,192.168.101.98:2006
lettuce:
pool:
min-idle: 8
max-idle: 16
max-active: 16
max-wait: 10000
案例一
如果不是用复杂的数据类型,用StringRedisTemplate就可以了,里面默认定义了序列化的方式,如果使用集群的reids,
需要保证key在同一个slot,可以用{xx}强制hash
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 抢红包
* 用户id 红包id
* @param userId
* @param redEnvelopeId
* @return
*/
@Override
public String grabRedEnvelope(Long userId, Long redEnvelopeId) {
DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
redisScript.setResultType(String.class);
redisScript.setScriptText(LuaScript.redLua);
List<String> keyList = new ArrayList<>();
/**
* 产生的小红包key
*/
keyList.add("{envelope}:redEnvelopeId:" + redEnvelopeId);
/**
* 红包领取记录key
*/
keyList.add("{envelope}:record:" + redEnvelopeId);
keyList.add("{envelope}" + userId);
//keyList.add(String.valueOf(userId));
/**
* -1 已经抢到红包 -2 红包已经完了 ,其余是抢到红包并返回红包余额
*/
return stringRedisTemplate.execute(redisScript, keyList,userId+"");
lua脚本
/**
* -1 已经抢到红包 -2 红包被抢光 re 红包金额 ,keys[1]、keys[2]、keys[3]分别为存储小红包的key、红包领取记录key、用户id
*/
public static String redLua = "if redis.call('hexists',KEYS[2],ARGV[1]) ~=0 then \n" +
" return '-1';\n" +
" else \n" +
"local re=redis.call('rpop',KEYS[1]);\n" +
"if re then\n" +
"redis.call('hset',KEYS[2],ARGV[1],1);\n" +
"return re;\n" +
"else\n" +
"return '-2';\n" +
"end\n" +
"end";
案例二
如果value是复杂数据类型,这个时候stringRedisTemplate就不了,需要自己定制RedisTemplate
@Configuration
public class MyRedisConf {
@Bean(name = "myRedisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
return template;
}
}
这里还是要指定返回参数的序列化类型,不然会报错
@Resource(name = "myRedisTemplate")
private RedisTemplate<String,Object> redisTemplate;
@RequestMapping("/test02")
public void test02(){
String script = "local receive_arg_json = cjson.decode(ARGV[1]);\n" +
"local v1 = receive_arg_json.v1;\n" +
"local v2 = receive_arg_json.v2;\n" +
"\n" +
"redis.call('set','wxy',KEYS[1]);" +
"return 'success';";
DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<>();
defaultRedisScript.setResultType(String.class);
defaultRedisScript.setScriptText(script);
List<String> keyList = new ArrayList<>();
keyList.add("{wxy}:a");
keyList.add("{wxy}:b");
Map<String,String> argvMap = new HashMap<>();
argvMap.put("v1","v1");
argvMap.put("v2","v2");
String execute = redisTemplate.execute(defaultRedisScript,redisTemplate.getValueSerializer(), RedisSerializer.string(), keyList, argvMap);
System.out.println("--------------------------"+execute);
}
案例三
@RequestMapping("/test03")
public void test03() {
String script = "--返回的变量\n" +
"local result = {}\n" +
"--获取KEY\n" +
"--开抢时间\n" +
"local key1 = KEYS[1]\n" +
"--过期时间\n" +
"local key2 = KEYS[2]\n" +
"--红包金额分配key\n" +
"local key3 = KEYS[3]\n" +
"--红包领取记录key\n" +
"local key4 = KEYS[4]\n" +
"--红包实体信息\n" +
"--local key5 = KEYS[5]\n" +
"\n" +
"--获取value ,占时是uid\n" +
"--local value1 = ARGV[1]\n" +
"--1.校验是否过期\n" +
"if( redis.call('ttl',key2) == -2)\n" +
"then\n" +
"result[1] = '1'\n" +
"result[2] = cjson.encode('红包已过期')\n" +
"return result;\n" +
"end\n" +
"\n" +
"--2.校验是否触发\n" +
"local trigger_time = redis.call('ttl',key1)\n" +
"if(trigger_time ~= -2)\n" +
"then\n" +
"result[1] = '2'\n" +
"result[2] = trigger_time\n" +
"return result;\n" +
"end\n" +
"\n" +
"--3.校验是否已经抢过\n" +
"if (redis.call('hexists',key4,ARGV[1]) ~=0) \n" +
"then\n" +
"result[1] = '3'\n" +
"result[2] = cjson.encode('已抢到红包')\n" +
"return result;\n" +
"else\n" +
"local re = redis.call('rpop',key3)\n" +
"if re\n" +
"then\n" +
"redis.call('hset',key4,ARGV[1],1)\n" +
"result[1] = '4'\n" +
"result[2] = re\n" +
"return result;\n" +
"else\n" +
"result[1] = '5'\n" +
"result[2] = cjson.encode('来慢了')\n" +
"return result;\n" +
"end\n" +
"end\n" +
"\n" +
" \n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n";
DefaultRedisScript<List> defaultRedisScript = new DefaultRedisScript<>();
defaultRedisScript.setResultType(List.class);
defaultRedisScript.setScriptText(script);
List<String> keyList = new ArrayList<>();
keyList.add(RED_ENVELOPE_LOCK_KEY);
keyList.add(RED_ENVELOPE_TTL_KEY);
keyList.add(RED_ENVELOPE_AMOUNT_KEY);
keyList.add(RED_ENVELOPE_RECORD);
List execute = redisTemplate.execute(defaultRedisScript, keyList, 123);
System.out.println("--------------------------" + execute);
}
参考
https://blog.csdn.net/clfvita/article/details/73955953
https://blog.csdn.net/fsw4848438/article/details/81540495?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control