【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件(二)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件

【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件(一)https://developer.aliyun.com/article/1471009


定义我们分布式锁的基础抽象类

接下来我们就定一下我们分布式锁的一个基础抽象类:AbstractDistributeLockSupport。这个类主要实现了我们之前的那个接口DistributeLockSupport。

java

复制代码

public abstract class AbstractDistributeLockSupport<T> implements DistributeLockSupport<T> {
    /**
     * 检验参数
     * @param distributeLockParam
     * @return
     */
    protected DistributeLockParam fullDistributeDefaultValue(DistributeLockParam distributeLockParam){
        Preconditions.checkNotNull(distributeLockParam,"检测到了参数不允许为空!");
        DistributeLockType distributeLockType = distributeLockParam.getLockType();
        distributeLockParam.setLockType(Optional.ofNullable(distributeLockType).orElse(DistributeLockType.FAIR_LOCK));
        distributeLockParam.setExpireTime(Optional.ofNullable(distributeLockParam.getExpireTime()).orElse(DEFAULT_EXPIRE_TIME));
        distributeLockParam.setWaitTime(Optional.ofNullable(distributeLockParam.getExpireTime()).orElse(DEFAULT_WAIT_TIME));
        distributeLockParam.setTimeUnit(Optional.ofNullable(distributeLockParam.getTimeUnit()).orElse(TimeUnit.SECONDS));
        return distributeLockParam;
    }
    /**
     * 构建相关的锁key值
     * @param distributeLockParam
     * @return
     */
    protected String buildLockKey(DistributeLockParam distributeLockParam){
        String lockId = StringUtils.defaultIfEmpty(distributeLockParam.getLockUUid(),
                        UUID.fastUUID().toString());
        distributeLockParam.setLockUUid(lockId);
        String delmiter = StringUtils.defaultIfEmpty(distributeLockParam.getDelimiter(),
                            DEFAULT_DELIMTER);
        distributeLockParam.setDelimiter(delmiter);
        String prefix = StringUtils.defaultIfEmpty(distributeLockParam
                .getLockNamePrefix(),DEFAULT_KEY_PREFIX);
        distributeLockParam.setLockNamePrefix(prefix);
        String lockFullName = "";
        if(!delmiter.equals(DEFAULT_DELIMTER)){
            //todo 待优化
            Joiner joiner = Joiner.on(delmiter).skipNulls();
            lockFullName = joiner.join(prefix,lockId);
        }else{
            lockFullName = DEFAULT_JOINER.join(prefix,lockId);
        }
        return lockFullName;
    }

该类主要包含两个方法。分别是fullDistributeDefaultValue和buildLockKey。

fullDistributeDefaultValue方法

这个方法主要的目的是为了校验以及填充一些我们没有写的参数的默认值。

java

复制代码

protected DistributeLockParam fullDistributeDefaultValue(DistributeLockParam distributeLockParam){
        Preconditions.checkNotNull(distributeLockParam,"检测到了参数不允许为空!");
        DistributeLockType distributeLockType = distributeLockParam.getLockType();
        distributeLockParam.setLockType(Optional.ofNullable(distributeLockType).orElse(DistributeLockType.FAIR_LOCK));
        distributeLockParam.setExpireTime(Optional.ofNullable(distributeLockParam.getExpireTime()).orElse(DEFAULT_EXPIRE_TIME));
        distributeLockParam.setWaitTime(Optional.ofNullable(distributeLockParam.getExpireTime()).orElse(DEFAULT_WAIT_TIME));
        distributeLockParam.setTimeUnit(Optional.ofNullable(distributeLockParam.getTimeUnit()).orElse(TimeUnit.SECONDS));
        return distributeLockParam;
}

buildLockKey方法

该类主要负责的是构建我们的分布式锁的key

java

复制代码

protected String buildLockKey(DistributeLockParam distributeLockParam){
        String lockId = StringUtils.defaultIfEmpty(distributeLockParam.getLockUUid(),
                        UUID.fastUUID().toString());
        distributeLockParam.setLockUUid(lockId);
        String delmiter = StringUtils.defaultIfEmpty(distributeLockParam.getDelimiter(),
                            DEFAULT_DELIMTER);
        distributeLockParam.setDelimiter(delmiter);
        String prefix = StringUtils.defaultIfEmpty(distributeLockParam
                .getLockNamePrefix(),DEFAULT_KEY_PREFIX);
        distributeLockParam.setLockNamePrefix(prefix);
        String lockFullName = "";
        if(!delmiter.equals(DEFAULT_DELIMTER)){
            //todo 待优化
            Joiner joiner = Joiner.on(delmiter).skipNulls();
            lockFullName = joiner.join(prefix,lockId);
        }else{
            lockFullName = DEFAULT_JOINER.join(prefix,lockId);
        }
        return lockFullName;
}

从在马上可以看出来,他主要就是将我们之前的那些所有的属性进行连接拼接到一起。

至此,我们的基础组建部分的抽象部分就已经完成了。那么接下来呢我们需要进行一个实现Redis模式下的分布式锁。


定义Redis分布式锁的注解

主要用于AOP的一个拦截以及获取一些特定化的参数。

RedisDistributedLock注解

java

复制代码

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RedisDistributedLock {
    String prefix() default "";
    /**
     * 锁过期时间
     */
    int expireTime() default 30;
    /**
     * 获取锁等待时间
     */
    int waitTime() default 10;
    TimeUnit timeUnit() default TimeUnit.SECONDS;
    String delimiter() default ":";
    LockCategory category() default LockCategory.COMMON;
}

RedisDistributedLockParam注解

主要用于参数方法上的修饰,获取参数相关的一些主要用于参数方法上的修饰,获取参数相关的一些参数值作为我们的分布式主键的key。

java

复制代码

@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RedisDistributedLockParam {
    String name() default "";
}

实现分布式锁的唯一键的生成器

我们定义分布式锁组件抽象接口的redis版本为RedisDistributedLockKeyGenerator。

java

复制代码

public class RedisDistributedLockKeyGenerator  implements LockKeyGenerator {
    @Override
    public String getLockKey(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        RedisDistributedLock lockAnnotation = method.getAnnotation(RedisDistributedLock.class);
        final Object[] args = pjp.getArgs();
        final Parameter[] parameters = method.getParameters();
        StringBuilder builder = new StringBuilder();
        // 默认解析方法里面带 CacheParam 注解的属性,如果没有尝试着解析实体对象中的
        for (int i = 0; i < parameters.length; i++) {
            final RedisDistributedLockParam annotation = parameters[i].getAnnotation(RedisDistributedLockParam.class);
            if (annotation == null) {
                continue;
            }
            builder.append(lockAnnotation.delimiter()).append(args[i]);
        }
        if (StringUtils.isEmpty(builder.toString())) {
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                final Object object = args[i];
                final Field[] fields = object.getClass().getDeclaredFields();
                for (Field field : fields) {
                    final RedisDistributedLockParam annotation = field.getAnnotation(RedisDistributedLockParam.class);
                    if (annotation == null) {
                        continue;
                    }
                    field.setAccessible(true);
                    builder.append(lockAnnotation.delimiter()).append(ReflectionUtils.getField(field, object));
                }
            }
        }
        return lockAnnotation.prefix() + builder.toString();
    }
}

上面的码主要是用鱼上面的代码主要是用鱼去提取注解上的参数以及一些呃参数的key的一个基本信息。

实现分布式锁实现类RedisDistributeLockSupport

RedisDistributeLockSupport主要实现了我们的抽象分布式锁的核心业务接口。

其中使用了Redisson的RedissonClient客户端服务,从而进行选择类型,进行选择分布式锁的方式。

java

复制代码

@Slf4j
@Component
public class RedisDistributeLockSupport extends AbstractDistributeLockSupport<RLock> {
    @Autowired
    RedissonClient redissonClient;
    /**
     * 非阻塞方式锁
     * @param distributeLockParam
     * @return
     */
    @Override
    public RLock lock(DistributeLockParam distributeLockParam) {
        distributeLockParam = fullDistributeDefaultValue(distributeLockParam);
        String lockKey = buildLockKey(distributeLockParam);
        RLock rLock = null;
        try {
            switch (distributeLockParam.getLockType()) {
                // 可重入锁
                case REENTRANT_LOCK: {
                    rLock = redissonClient.getLock(lockKey);
                    break;
                }
                // 非公平锁
                case FAIR_LOCK: {
                    rLock = redissonClient.getFairLock(lockKey);
                    break;
                }
                default: {
                    throw new UnsupportedOperationException("暂时不支持此种方式的锁!");
                }
            }
            Boolean result = rLock.tryLock(distributeLockParam.getWaitTime(), distributeLockParam.getExpireTime(), distributeLockParam.getTimeUnit());
            return rLock;
        } catch (InterruptedException e) {
            log.error("加锁为阻塞模式下的锁进行失败!", e);
            return rLock;
        }
    }
    @Override
    public void unlock(RLock param, DistributeLockParam distributeLockParam) {
        try {
            param.unlock();
        } catch (Exception e) {
            log.error("解我操!啊?锁为阻塞模式下的锁进行失败!", e);
        }
    }
}

可以根据我们所的类型选择。公平锁或者是和重入锁。

实现分布式锁实现类RedisDistributedLockAspect切面类

java

复制代码

@Aspect
@Order(4)
@Slf4j
public class RedisDistributedLockAspect {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisDistributedLockKeyGenerator redisDistributedLockKeyGenerator;
    @Around("execution(public * *(..)) && @annotation(com.hyts.assemble.distributeLock.redis.RedisDistributedLock)")
    public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        RedisDistributedLock redisDistributedLock = method.getAnnotation(RedisDistributedLock.class);
        if (StringUtils.isEmpty(redisDistributedLock.prefix())) {
            throw new RuntimeException("lock key can't be null...");
        }
        final String lockKey = redisDistributedLockKeyGenerator.getLockKey(pjp);
        RLock lock = chooseLock(redisDistributedLock,lockKey);
        //key不存在才能设置成功
        Boolean success = null;
        Object proceed = null;
        try {
            success = lock.tryLock(redisDistributedLock.waitTime(), redisDistributedLock.expireTime(), redisDistributedLock.timeUnit());
            if (success) {
                log.debug("tryLock success key [{}]", lockKey);
                proceed = pjp.proceed();
            } else {
                log.error("key is : {" + lockKey + "} tryLock fail ");
                throw new RedisDistributedLockException(lockKey);
            }
        } catch (InterruptedException e) {
            log.error("key is : {" + lockKey + "} tryLock error ", e);
            throw new RedisDistributedLockException(lockKey, e.getMessage());
        } finally {
            if (success) {
                log.debug("unlock [{}]", "key:" + lockKey);
                lock.unlock();
            }
        }
        return proceed;
    }
    private RLock chooseLock(RedisDistributedLock redisDistributedLock, String lockName) {
        LockCategory category = redisDistributedLock.category();
        switch (category) {
            case FAIR:
                return redissonClient.getFairLock(lockName);
        }
        return redissonClient.getLock(lockName);
    }
}



相关实践学习
基于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月前
|
弹性计算 负载均衡 网络协议
阿里云SLB深度解析:从流量分发到架构优化的技术实践
本文深入探讨了阿里云负载均衡服务(SLB)的核心技术与应用场景,从流量分配到架构创新全面解析其价值。SLB不仅是简单的流量分发工具,更是支撑高并发、保障系统稳定性的智能中枢。文章涵盖四层与七层负载均衡原理、弹性伸缩引擎、智能DNS解析等核心技术,并结合电商大促、微服务灰度发布等实战场景提供实施指南。同时,针对性能调优与安全防护,分享连接复用优化、DDoS防御及零信任架构集成的实践经验,助力企业构建面向未来的弹性架构。
200 76
|
1月前
|
人工智能 自然语言处理 API
MCP与A2A协议比较:人工智能系统互联与协作的技术基础架构
本文深入解析了人工智能领域的两项关键基础设施协议:模型上下文协议(MCP)与代理对代理协议(A2A)。MCP由Anthropic开发,专注于标准化AI模型与外部工具和数据源的连接,降低系统集成复杂度;A2A由Google发布,旨在实现不同AI代理间的跨平台协作。两者虽有相似之处,但在设计目标与应用场景上互为补充。文章通过具体示例分析了两种协议的技术差异及适用场景,并探讨了其在企业工作流自动化、医疗信息系统和软件工程中的应用。最后,文章强调了整合MCP与A2A构建协同AI系统架构的重要性,为未来AI技术生态系统的演进提供了方向。
484 62
|
1月前
|
机器学习/深度学习 传感器 自然语言处理
基于Transformer架构的时间序列数据去噪技术研究
本文介绍了一种基于Transformer架构的时间序列去噪模型。通过生成合成数据训练,模型在不同噪声条件下展现出强去噪能力。文章详细解析了Transformer的输入嵌入、位置编码、自注意力机制及前馈网络等关键组件,并分析实验结果与注意力权重分布。研究为特定任务的模型优化和专业去噪模型开发奠定了基础。
130 14
基于Transformer架构的时间序列数据去噪技术研究
|
29天前
|
存储 消息中间件 SQL
数据中台架构与技术体系
本文介绍了数据中台的整体架构设计,涵盖数据采集、存储、计算、服务及治理等多个层面。在数据采集层,通过实时与离线方式整合多类型数据源;存储层采用分层策略,包括原始层、清洗层、服务层和归档层,满足不同访问频率需求;计算层提供批处理、流处理、交互式分析和AI计算能力,支持多样化业务场景。数据服务层封装数据为标准化API,实现灵活调用,同时强调数据治理与安全,确保元数据管理、质量监控、权限控制及加密措施到位,助力企业构建高效、合规的数据管理体系。
|
24天前
|
存储 机器学习/深度学习 算法
阿里云X86/ARM/GPU/裸金属/超算等五大服务器架构技术特点、场景适配与选型策略
在我们选购阿里云服务器的时候,云服务器架构有X86计算、ARM计算、GPU/FPGA/ASIC、弹性裸金属服务器、高性能计算可选,有的用户并不清楚他们之间有何区别。本文将深入解析这些架构的特点、优势及适用场景,帮助用户更好地根据实际需求做出选择。
|
1月前
|
Cloud Native 关系型数据库 分布式数据库
登顶TPC-C|云原生数据库PolarDB技术揭秘:Limitless集群和分布式扩展篇
云原生数据库PolarDB技术揭秘:Limitless集群和分布式扩展篇
|
2月前
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
203 0
分布式爬虫框架Scrapy-Redis实战指南
|
3月前
|
NoSQL Java 中间件
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
本文介绍了从单机锁到分布式锁的演变,重点探讨了使用Redis实现分布式锁的方法。分布式锁用于控制分布式系统中多个实例对共享资源的同步访问,需满足互斥性、可重入性、锁超时防死锁和锁释放正确防误删等特性。文章通过具体示例展示了如何利用Redis的`setnx`命令实现加锁,并分析了简化版分布式锁存在的问题,如锁超时和误删。为了解决这些问题,文中提出了设置锁过期时间和在解锁前验证持有锁的线程身份的优化方案。最后指出,尽管当前设计已解决部分问题,但仍存在进一步优化的空间,将在后续章节继续探讨。
612 131
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
|
12天前
|
数据采集 存储 NoSQL
分布式爬虫去重:Python + Redis实现高效URL去重
分布式爬虫去重:Python + Redis实现高效URL去重
|
3月前
|
NoSQL Java Redis
Springboot使用Redis实现分布式锁
通过这些步骤和示例,您可以系统地了解如何在Spring Boot中使用Redis实现分布式锁,并在实际项目中应用。希望这些内容对您的学习和工作有所帮助。
245 83

热门文章

最新文章