注解的方式实现redis分布式锁

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 创建redisLock注解:import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.

创建redisLock注解:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * redis锁注解
 * @author MAZHEN
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface RedisLock {
    String lockPrefix() default "";
    long timeOut() default 60;
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

拦截器逻辑:

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.apache.bcel.classfile.Constant;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.spring.mongo.annotation.RedisLock;

/**
 * redis锁拦截器实现
 * @author MAZHEN
 */
@Aspect
@Component
public class RedisLockInterceptor {

    private static final Integer MAX_RETRY_COUNT = 10;
    private static final String LOCK_PRE_FIX = "lockPreFix";
    private static final String TIME_OUT = "timeOut";

    @Autowired
    private RedisManager redisManager;

    @Pointcut("@annotation(com.spring.mongo.annotation.RedisLock)")
    public void redisLockAspect(){}

    @Around("redisLockAspect()")
    public Map<String, Object> lockAroundAction(ProceedingJoinPoint proceeding){
        //获取redis锁
        Map<String, Object> getLockResult = this.getLock(proceeding,0,System.currentTimeMillis());
    }

    /**
     * 获取锁
     * @param proceeding
     * @return
     */
    private Map<String, Object> getLock(ProceedingJoinPoint proceeding,int count,long currentTime){
        //获取注解中的参数
        Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding);
        String lockPrefix = (String) annotationArgs.get(LOCK_PRE_FIX);
        long expire = (long) annotationArgs.get(TIME_OUT);
        String key = this.getFirstArg(proceeding);
        if(StringUtils.isEmpty(lockPrefix) || StringUtils.isEmpty(key)){
            return this.argErrResult("锁前缀或业务参数不能为空");
        }
        String lockName = lockPrefix+"_"+key;
        String value = String.valueOf(currentTime);
        if(CommonRedisUtils.setNx(lockName,value) == 1){
            //获取锁成功
            CommonRedisUtils.expire(lockName,expire);
            return this.buildSuccessResult();
        }else {
            //获取锁失败,为防止其它线程正在设置过时时间时误删,添加第一个条件
            if((System.currentTimeMillis()-currentTime>5000)
                    &&(CommonRedisUtils.ttl(lockName)<0
                            ||System.currentTimeMillis()-currentTime>expire)){
                //强制删除锁,并尝试再次获取锁
                CommonRedisUtils.delete(lockName);
                if(count<MAX_RETRY_COUNT){
                    return getLock(proceeding,count++,currentTime);
                }
            }
            return this.buildGetLockErrorResult("请重试!!!");
        }
    }

    /**
     * 获取锁参数
     * @param proceeding
     * @return
     */
    private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding){
        Class target = proceeding.getTarget().getClass();
        Method[] methods = target.getMethods();
        String methodName = proceeding.getSignature().getName();
        for (Method method : methods) {
            if(method.getName().equals(methodName)){
                Map<String, Object> result = new HashMap<String, Object>();
                RedisLock redisLock = method.getAnnotation(RedisLock.class);
                result.put(LOCK_PRE_FIX,redisLock.lockPrefix());
                result.put(TIME_OUT, redisLock.timeUnit().toSeconds(redisLock.timeOut()));
                return result;
            }
        }
        return null;
    }

    /**
     * 获取第一个String类型的参数为锁的业务参数
     * @param proceeding
     * @return
     */
    public String getFirstArg(ProceedingJoinPoint proceeding){
        Object[] args = proceeding.getArgs();
        if(args != null && args.length>0){
            for (Object object : args) {
                String type = object.getClass().getName();
                if("java.lang.String".equals(type)){
                    return (String)object;
                }
            }
        }
        return null;
    }

    public Map<String, Object> argErrResult(String mes){
        Map<String, Object> result = new HashMap<String, Object>();
        //TODO
        //result.put("code", "9");
        result.put("msg", mes);
        return result;
    }

    public Map<String, Object> buildGetLockErrorResult(String mes){
        Map<String, Object> result = new HashMap<String, Object>();
        //TODO
        //result.put("code", "9");
        result.put("msg", mes);
        return result;
    }

    public Map<String, Object> buildSuccessResult(){
        Map<String, Object> result = new HashMap<String, Object>();
        //TODO
        //result.put("code", "1");
        result.put("msg", "处理成功");
        return result;
    }
}

使用方式:只需要在需要使用redis锁的方法上添加@RedisLock注解,并输入redis锁的前缀字段,过时时间和时间单位有默认值,而方法上的第一个String类型的参数为锁的key的第二段。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
NoSQL 算法 安全
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
152 0
|
1月前
|
NoSQL 关系型数据库 MySQL
分布式锁(redis/mysql)
分布式锁(redis/mysql)
58 1
|
27天前
|
NoSQL Java Redis
如何通俗易懂的理解Redis分布式锁
在多线程并发的情况下,我们如何保证一个代码块在同一时间只能由一个线程访问呢?
36 2
|
1月前
|
人工智能 监控 NoSQL
【万字长文 一文搞定】Redis:从新手村到大师殿堂的奥德赛之旅 9种实现分布式锁的全技术指南
【万字长文 一文搞定】Redis:从新手村到大师殿堂的奥德赛之旅 9种实现分布式锁的全技术指南
81 4
|
12天前
|
NoSQL Linux Redis
06- 你们使用Redis是单点还是集群 ? 哪种集群 ?
**Redis配置:** 使用哨兵集群,结构为1主2从,加上3个哨兵节点,总计分布在3台Linux服务器上,提供高可用性。
27 0
|
21天前
|
负载均衡 监控 NoSQL
Redis的集群方案有哪些?
Redis集群包括主从复制(基础,手动故障恢复)、哨兵模式(自动高可用)和Redis Cluster(官方分布式解决方案,自动分片和容错)。此外,还有如Codis、Redisson和Twemproxy等第三方工具用于代理和负载均衡。选择方案需考虑应用场景、数据规模和并发需求。
25 2
|
27天前
|
NoSQL Redis
Redis集群(六):集群常用命令及说明
Redis集群(六):集群常用命令及说明
23 0
|
2月前
|
运维 NoSQL 算法
Redis-Cluster 与 Redis 集群的技术大比拼
Redis-Cluster 与 Redis 集群的技术大比拼
46 0
|
21天前
|
NoSQL Java 测试技术
面试官:如何搭建Redis集群?
**Redis Cluster** 是从 Redis 3.0 开始引入的集群解决方案,它分散数据以减少对单个主节点的依赖,提升读写性能。16384 个槽位分配给节点,客户端通过槽位信息直接路由请求。集群是无代理、去中心化的,多数命令直接由节点处理,保持高性能。通过 `create-cluster` 工具快速搭建集群,但适用于测试环境。在生产环境,需手动配置文件,启动节点,然后使用 `redis-cli --cluster create` 分配槽位和从节点。集群动态添加删除节点、数据重新分片及故障转移涉及复杂操作,包括主从切换和槽位迁移。
31 0
面试官:如何搭建Redis集群?
|
25天前
|
存储 缓存 NoSQL
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群功能分析)(一)
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群功能分析)
49 0