封装redis 分布式锁 RedisCallback

RedisCallback 是redis 一个回调接口,在 Redis 连接后执行单个命令,返回执行命令后的结果。 如果在使用 RedisCallback 时,需要自动获取 Redis 连接资源,使用完毕后并释放连接资源。

RedisTemplate 类提供了一个 execute 方法,用于执行 Redis 命令并返回执行命令后的结果。

1.lock类方法封装的方法:

java 复制代码
package com.ecarx.check.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 类描述 <p>
 * Copyright: Copyright © 2021 ECARX Co., Ltd. All Rights Reserved. <p>
 * Company: xxx科技有限公司<p>
 *
 * @author qb
 * @since 2023/8/30 14:34
 */
@Slf4j
@Component
public class RedisLock {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 释放锁脚本,原子操作,lua脚本
     */
    private static final String UNLOCK_LUA;
    /**
     * 默认过期时间(30ms)
     */
    private static final long DEFAULT_EXPIRE = 30L;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();
    }

    /**
     * 获取分布式锁,原子操作
     * @param lockKey   锁
     * @param lockValue 唯一ID, 可以使用UUID.randomUUID().toString();
     * @return 是否枷锁成功
     */
    public boolean lock(String lockKey, String lockValue) {
        return this.lock(lockKey, lockValue, DEFAULT_EXPIRE, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取分布式锁,原子操作
     * @param lockKey   锁
     * @param lockValue 唯一ID, 可以使用UUID.randomUUID().toString();
     * @param expire    过期时间
     * @param timeUnit  时间单位
     * @return 是否枷锁成功
     */
    public boolean lock(String lockKey, String lockValue, long expire, TimeUnit timeUnit) {
        try {
            RedisCallback callback = (connection) -> connection.set(lockKey.getBytes(StandardCharsets.UTF_8),
                    lockValue.getBytes(StandardCharsets.UTF_8), Expiration.seconds(timeUnit.toSeconds(expire)),
                    RedisStringCommands.SetOption.SET_IF_ABSENT);
            return (boolean) redisTemplate.execute(callback);
        } catch (Exception e) {
            log.error("redis lock error ,lock key: {}, value : {}, error info : {}", lockKey, lockValue, e);
        }
        return false;
    }

    /**
     * 释放锁
     * @param lockKey   锁
     * @param lockValue 唯一ID
     * @return 执行结果
     */
    public boolean unlock(String lockKey, String lockValue) {
        RedisCallback callback = (connection) -> connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN, 1, lockKey.getBytes(StandardCharsets.UTF_8), lockValue.getBytes(StandardCharsets.UTF_8));
        return (boolean) redisTemplate.execute(callback);
    }

    /**
     * 获取Redis锁的value值
     * @param lockKey 锁
     */
    public String get(String lockKey) {
        try {
            RedisCallback callback = (connection) -> new String(Objects.requireNonNull(connection.get(lockKey.getBytes())), StandardCharsets.UTF_8);
            return (String) redisTemplate.execute(callback);
        } catch (Exception e) {
            log.error("get redis value occurred an exception,the key is {}, error is {}", lockKey, e);
        }
        return null;
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }
}

2.使用分布式锁

java 复制代码
String taskId = "123456";
 for (int i = 0; i < 20; i++) {
    boolean check = redisLock.lock("CHECK_MM_FINISH_BATCH_" + taskId, uuid, 60, TimeUnit.SECONDS);
    if (check) {
            try {
               /*
               * 业务逻辑处理
               */
            } finally {
                // 释放锁
                redisLock.unlock("CHECK_MDB_FINISH_BATCH_" + taskId, uuid);
                log.info("释放锁, 服务:taskId:{}", taskId);
            }
             break;
            } else {
                log.info("更新完成状态抢锁失败, taskId:{}, batchId:{}", taskId, batchId);
                // 每次循环延迟三秒
                Thread.sleep(3000);
            }
}
相关推荐
C++忠实粉丝19 分钟前
Redis 介绍和安装
数据库·redis·缓存
李洋-蛟龙腾飞公司19 分钟前
HarmonyOS Next 应用元服务开发-分布式数据对象迁移数据文件资产迁移
分布式·华为·harmonyos
ClouGence1 小时前
Redis 到 Redis 数据迁移同步
数据库·redis·缓存
苏三说技术1 小时前
Redis 性能优化的18招
数据库·redis·性能优化
Tttian6222 小时前
基于Pycharm与数据库的新闻管理系统(2)Redis
数据库·redis·pycharm
言之。2 小时前
redis延迟队列
redis
技术路上的苦行僧3 小时前
分布式专题(10)之ShardingSphere分库分表实战指南
分布式·shardingsphere·分库分表
hanbarger3 小时前
nosql,Redis,minio,elasticsearch
数据库·redis·nosql
弗罗里达老大爷3 小时前
Redis
数据库·redis·缓存
GitCode官方4 小时前
GitCode 光引计划投稿 | GoIoT:开源分布式物联网开发平台
分布式·开源·gitcode