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

本文涉及的产品
云原生内存数据库 Tair,内存型 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
相关文章
|
2天前
|
Kubernetes API 调度
Kubernetes 架构解析:理解其核心组件
【8月更文第29天】Kubernetes(简称 K8s)是一个开源的容器编排系统,用于自动化部署、扩展和管理容器化应用。它提供了一个可移植、可扩展的环境来运行分布式系统。本文将深入探讨 Kubernetes 的架构设计,包括其核心组件如何协同工作以实现这些功能。
13 0
|
2天前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
4天前
|
Kubernetes Cloud Native 开发者
云原生技术在现代IT架构中的应用与挑战
【8月更文挑战第27天】 随着云计算的飞速发展,云原生技术已经成为推动企业数字化转型的重要力量。本文将深入探讨云原生技术的核心概念、优势以及在实际应用中遇到的挑战,并通过具体代码示例展示如何利用云原生技术优化IT架构。
|
3天前
|
安全 网络安全 数据安全/隐私保护
云原生技术探索:容器化与微服务架构的实践之路网络安全与信息安全:保护数据的关键策略
【8月更文挑战第28天】本文将深入探讨云原生技术的核心概念,包括容器化和微服务架构。我们将通过实际案例和代码示例,展示如何在云平台上实现高效的应用部署和管理。文章不仅提供理论知识,还包含实操指南,帮助开发者理解并应用这些前沿技术。 【8月更文挑战第28天】在数字化时代,网络安全和信息安全是保护个人和企业数据的前线防御。本文将探讨网络安全漏洞的成因、加密技术的应用以及提升安全意识的重要性。文章旨在通过分析网络安全的薄弱环节,介绍如何利用加密技术和提高用户警觉性来构建更为坚固的数据保护屏障。
|
4天前
|
存储 安全 虚拟化
深入解析:Docker的架构与组件
【8月更文挑战第27天】
57 2
|
5天前
|
JSON 前端开发 API
Django 后端架构开发:通用表单视图、组件对接、验证机制和组件开发
Django 后端架构开发:通用表单视图、组件对接、验证机制和组件开发
27 2
|
3天前
|
架构师 NoSQL 中间件
挑战架构师极限:分布式锁的四种实现方式,优劣对比让你一目了然!
【8月更文挑战第29天】在2024年软考架构师考试中,掌握分布式锁的实现方法极其重要。本文详细介绍了基于数据库、Redis及ZooKeeper三种常见分布式锁方案。数据库锁简单易懂但性能低;Redis锁性能优越且支持自动续期,但需引入中间件;ZooKeeper锁可靠性高,适用于分布式环境,但实现复杂。通过对比各方案优缺点,帮助考生更好地应对考试,选择最适合业务场景的分布式锁策略。
|
5天前
|
存储 缓存 数据管理
Django后端架构开发:后台管理与会话技术详解
Django后端架构开发:后台管理与会话技术详解
16 0
|
7天前
|
存储 虚拟化 网络虚拟化
|
7天前
|
存储 容灾 Cloud Native
应用多活技术问题之支撑应用构建多活架构能力如何解决
应用多活技术问题之支撑应用构建多活架构能力如何解决

热门文章

最新文章

下一篇
云函数