1. Benefits of using Lua scripts
- Reduce network overhead: the operation of the original 5 network requests can be completed with one request, and the logic of the original 5 requests can be completed on the Redis server. Scripts are used to reduce network round-trip latency.
- Atomic operations: Redis will execute the entire script as a whole and will not be inserted by other commands (Java and other clients will execute multiple commands to complete a business, violating atomic operations).
- Reuse: Scripts sent by clients are permanently stored in Redis, meaning that other clients can reuse the script without having to use code to complete the same logic.
- Redis builds the Lua interpreter on the server side (version 2.6 and up)
- Redis – THE CLI provides EVAL and EVALSHA commands to execute Lua scripts
2, implementation,
2.1. Script preparation
lock.lua
if redis.call('exists', KEYS[1= =])0
then
redis.call('hset', KEYS[1], ARGV[1].1);
redis.call('expire', KEYS[1], ARGV[2]);
return 1;
end
if redis.call('hexists', KEYS[1], ARGV[1= =])1
then
redis.call('hincrby', KEYS[1], ARGV[1].1);
redis.call('expire', KEYS[1], ARGV[2]);
return 1;
end
return 0
Copy the code
unLock.lua
if redis.call('hexists',KEYS[1],ARGV[1= =])1
then
redis.call('del',KEYS[1]);
return 1;
else
return 0;
end
Copy the code
2.2. RedisLock implementation
/** * Redis implements distributed locking based on hset lua */
@Component
public class RedisLock {
private static ResourceLoader resourceLoader = new DefaultResourceLoader();
private static final int DEFAULT_TIME_OUT = 10; // 30 s Indicates the default validity period
private static final int DEFAULT_RELOCK_TIME = 3; // 10 s Execution time of the renewal period
private static RedisSerializer<String> argsSerializer = new StringRedisSerializer();
private static RedisSerializer<String> resultSerializer = new StringRedisSerializer();
private static RedisTemplate<String, String> redisTemplate;
private static Redisson redisson;
/** * resolve static attribute usage@AutowiredInjection *@param redisTemplate
*/
@Autowired
public RedisLock(RedisTemplate<String, String> redisTemplate, Redisson redisson){
this.redisTemplate = redisTemplate;
this.redisson = redisson;
}
/ / acquiring a lock
public static boolean myTryLock(String lockKey, String lockValue){
try {
// Get the lua script
DefaultRedisScript lockRedisScript =new DefaultRedisScript<>();
lockRedisScript.setLocation(new ClassPathResource("script/lock.lua"));
/ / this value type with lua return value type is consistent, otherwise it will be submitted to the Java. Lang. An IllegalStateException
lockRedisScript.setResultType(Long.class);
List<String> keys = Collections.singletonList(lockKey);
Object result = redisTemplate.execute(lockRedisScript, argsSerializer, resultSerializer, keys, lockValue, String.valueOf(DEFAULT_TIME_OUT));
if("1".equals(String.valueOf(result))){
// The lock is successfully obtained
autoWatchDog(lockKey, lockValue);
return true; }}catch (Exception e){
e.printStackTrace();
}
return false;
}
// Release the lock (lua script can only delete its own lock)
public static boolean myUnLock(String lockKey, String lockValue){
try {
// Get the lua script
DefaultRedisScript unLockRedisScript = new DefaultRedisScript<>();
unLockRedisScript.setLocation(new ClassPathResource("script/unLock.lua"));
/ / this value type with lua return value type is consistent, otherwise it will be submitted to the Java. Lang. An IllegalStateException
unLockRedisScript.setResultType(Long.class);
List<String> keys = Collections.singletonList(lockKey);
Object result = redisTemplate.execute(unLockRedisScript, argsSerializer, resultSerializer, keys, lockValue);
if("1".equals(String.valueOf(result))){
return true; }}catch (Exception e){
e.printStackTrace();
}
return false;
}
public static void autoWatchDog(String lockKey, String lockValue){
new Thread(new WatchDog(lockKey, lockValue)).start();
}
// Implement automatic renewal, watchdog
@Data
static class WatchDog implements Runnable{
private String lockKey;
private String lockValue;
public WatchDog(String lockKey, String lockValue){
this.lockKey = lockKey;
this.lockValue = lockValue;
}
@Override
public void run(a) {
System.out.println(lockValue + "-"+ new SimpleDateFormat("hh:mm:ss").format(new Date()) + "-" + "- Watchdog process started -----");
try {
Timer timer = new Timer();
timer.schedule(
new TimerTask() {
public void run(a) {
// Get the lua script
DefaultRedisScript lockRedisScript = new DefaultRedisScript<>();
lockRedisScript.setLocation(new ClassPathResource("script/lock.lua"));
/ / this value type with lua return value type is consistent, otherwise it will be submitted to the Java. Lang. An IllegalStateException
lockRedisScript.setResultType(Long.class);
List<String> keys = Collections.singletonList(lockKey);
Object result = redisTemplate.execute(lockRedisScript, argsSerializer, resultSerializer, keys, lockValue, String.valueOf(DEFAULT_TIME_OUT));
if(!"1".equals(String.valueOf(result))){
System.out.println(lockValue + "-"+ new SimpleDateFormat("hh:mm:ss").format(new Date()) + "-"+ "- Watchdog process terminated -----");
timer.cancel();
}else{
// Get the key expiration time
Long expire = redisTemplate.opsForValue().getOperations().getExpire(lockKey);
System.err.println(lockValue + "-"+ new SimpleDateFormat("hh:mm:ss").format(new Date()) + "-" + "- Watchdog process renewed successfully, remaining time -----"+ expire); }}},0, RedisLock.DEFAULT_RELOCK_TIME * 1000);
}catch(Exception e){ e.printStackTrace(); }}}// Get value based on key
public static String myGetValueByKey(String lockKey){
return redisTemplate.opsForValue().get(lockKey);
}
/ / redisson acquiring a lock
public static RLock redissonGetLock(String lockKey){
return redisson.getLock(lockKey);
}
/ / redisson releases the lock
public static void redissonUnLock(RLock rLock){
rLock.unlock();
}
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(
new TimerTask() {
public void run(a) {
System.out.println(new SimpleDateFormat("hh:mm:ss").format(new Date()) + "-" +11111); }},0.3000); }}Copy the code
2.3. Test classes
/** * Test redis distributed lock */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestRedisLock {
final static String lockKey = "jiaobaba";
public TestRedisLock(a) {
// Write this on the constructor
System.setProperty("es.set.netty.runtime.available.processors"."false");
}
@Test
public void test(a) throws InterruptedException {
CountDownLatch downLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
String lockValue = UUID.randomUUID().toString();
new Thread(()->{
while(! func(lockValue)){};// func1();
downLatch.countDown();
}).start();
}
downLatch.await();
System.out.println("All missions completed.");
}
/** * Implement your own distributed lock test *@param lockValue
* @return* /
public boolean func(String lockValue){
Boolean flag = true;
try{
/ / acquiring a lock
flag = RedisLock.myTryLock(lockKey, lockValue);
if(! flag){return false;
}
System.out.println(lockValue + "-"+ new SimpleDateFormat("hh:mm:ss").format(new Date()) + "-" + "- Lock obtained successfully, service started");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(lockValue + "-"+ new SimpleDateFormat("hh:mm:ss").format(new Date()) + "-" + "- Business completed");
}catch (Exception e){
e.printStackTrace();
}finally {
/ / releases the lock
boolean unLock = RedisLock.myUnLock(lockKey, lockValue);
if(unLock){
System.out.println(lockValue + "-"+ new SimpleDateFormat("hh:mm:ss").format(new Date()) + "-" + "- Release lock after service execution"); }}// Indicates that the lock is successfully obtained
return flag;
}
/** * redisson distributed lock test */
public void func1(a){
RLock rLock = RedisLock.redissonGetLock(lockKey);
try{
/ / lock
rLock.lock();
System.out.println(Thread.currentThread().getName() + "Lock obtained successfully. Service started.");
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}finally {
/ / releases the lock
RedisLock.redissonUnLock(rLock);
System.out.println(Thread.currentThread().getName() + "Release the lock after service execution."); }}}Copy the code