springboot项目创建笔记28 之《redis分布式锁》

一、添加工具类
RedisLockUtil.java

package com.example.utils;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

/**
 * redis分布式锁
 * 
 * @author user
 *
 */
@Component
@Slf4j
public class RedisLockUtil {

	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	/**
	 * 加锁
	 * 
	 * @param key
	 * @param value
	 * @param expireTime
	 * @param timeUnit
	 * @return
	 */
	public boolean lock(String key, String value, int expireTime, TimeUnit timeUnit) {
		// key不存在则创建存储key,key存在则返回null
		Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, value, expireTime, timeUnit);
		if (flag == null || flag == false) {
			log.info("锁({},{})失败,该锁已存在或加锁失败", key, value);
			return false;
		} else {
			log.info("锁({},{})成功", key, value);
			return true;
		}
	}

	/**
	 * 释放锁
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean unlock(String key, String value) {
		// 使用lua脚本保证原子性
		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
		RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
		Long result = (Long) stringRedisTemplate.execute(redisScript, Arrays.asList(key), value);
		if (result == null || result == 0) {
			log.info("释放锁({},{})失败,该锁不存在或锁已经过期", key, value);
			return false;
		} else {
			log.info("释放锁({},{})成功", key, value);
			return true;
		}
	}
}

说明:
1、这里注入的操作类是StringRedisTemplate,因为自己定义的RedisTemplate设置了redis键值对<K,V>的value序列化方式被设置成了json格式,所以程序set进redis的string类型值,序列化后会带两个引号。在后面执行lua脚本比较keys和argv时会有问题
2、释放锁时用了lua脚本,保证原子性
3、stringRedisTemplate.execute的value参数是String类型,不能传list
4、集群模式下keys只能传一个,否则会报错:CROSSSLOT Keys in request don't hash to the same slot,所有key必须在同一个哈希槽上

Constants.java添加一个前缀

public static final String LOCK_KEY = "lock:";

 二、测试类
RedisLockTest.java

package myboot;

import java.util.concurrent.TimeUnit;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.base.Constants;
import com.example.myboot.MybootApplication;
import com.example.utils.RedisLockUtil;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MybootApplication.class)
public class RedisLockTest {

	@Autowired
	RedisLockUtil redisLock;

	@Test
	public void redisLockTest() {
		String key = Constants.LOCK_KEY + "123";
		String value = "123";
		redisLock.lock(key, value, 3000, TimeUnit.SECONDS);
	}

	@Test
	public void redisUnlockTest() {
		String key = Constants.LOCK_KEY + "123";
		String value = "123";
		redisLock.unlock(key, value);
	}

}

参考资料:
https://blog.csdn.net/qq_33591903/article/details/108278059
https://blog.csdn.net/fedorafrog/article/details/112893769

注:最新代码上传至https://github.com/csj50/myboot
 

猜你喜欢

转载自blog.csdn.net/csj50/article/details/120187650
今日推荐