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

本文涉及的产品
云数据库 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
相关文章
|
8天前
|
存储 运维 关系型数据库
2024年最全ceph的功能组件和架构概述(2),Linux运维工程面试问题
2024年最全ceph的功能组件和架构概述(2),Linux运维工程面试问题
2024年最全ceph的功能组件和架构概述(2),Linux运维工程面试问题
|
12天前
|
消息中间件 分布式计算 中间件
秀出天际!阿里甩出的988页分布式微服务架构进阶神仙手册我粉了
秀出天际!阿里甩出的988页分布式微服务架构进阶神仙手册我粉了
|
14天前
|
存储 分布式计算 分布式数据库
【专栏】云计算与分布式系统架构在数字化时代的关键作用。云计算,凭借弹性、可扩展性和高可用性,提供便捷的计算环境
【4月更文挑战第27天】本文探讨了云计算与分布式系统架构在数字化时代的关键作用。云计算,凭借弹性、可扩展性和高可用性,提供便捷的计算环境;分布式系统架构则通过多计算机协同工作,实现任务并行和容错。两者相互依存,共同推动企业数字化转型、科技创新、公共服务升级及数字经济发展。虚拟化、分布式存储和计算、网络技术是其核心技术。未来,深化研究与应用这些技术将促进数字化时代的持续进步。
|
11天前
|
前端开发 Android开发
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
|
13天前
|
缓存 负载均衡 监控
探索分布式系统演进之路:从负载均衡到微服务架构
小米分享了分布式系统的发展,从早期的负载均衡(入口级、网关和客户端)到微服务架构的演进。微服务实现服务解耦,增强系统弹性,但带来了新的挑战。为优化数据库性能,实施了主备读写分离、全文搜索引擎、缓存集群等措施。通过微服务治理,如服务注册、动态配置、灰度发布等,提升了系统稳定性和可靠性。未来将继续优化分布式系统,提供更好的服务体验。关注公众号“软件求生”了解更多。
36 6
|
14天前
|
Kubernetes API 调度
Kubernetes学习-核心概念篇(二) 集群架构与组件
Kubernetes学习-核心概念篇(二) 集群架构与组件
|
14天前
|
运维 Cloud Native 持续交付
构建未来:以云原生为基石的分布式系统架构深入理解操作系统的内存管理机制
【4月更文挑战第30天】 随着企业数字化转型的不断深入,传统的IT架构已难以满足市场对于敏捷性、可扩展性和成本效益的需求。云原生技术作为推动这一变革的关键因素,其设计理念和实现方式正在重塑软件开发和运维模式。本文将探讨云原生架构的核心组件,包括容器化、微服务、持续集成/持续部署(CI/CD)、以及无服务器计算等,并分析其在构建分布式系统中的作用与挑战。通过实际案例,我们将展示如何利用云原生技术构建高效、弹性和可维护的分布式系统。
|
14天前
|
缓存 NoSQL Java
【亮剑】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护,如何使用注解来实现 Redis 分布式锁的功能?
【4月更文挑战第30天】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护。基于 Redis 的分布式锁利用 SETNX 或 SET 命令实现,并考虑自动过期、可重入及原子性以确保可靠性。在 Java Spring Boot 中,可通过 `@EnableCaching`、`@Cacheable` 和 `@CacheEvict` 注解轻松实现 Redis 分布式锁功能。
|
14天前
|
负载均衡 应用服务中间件 nginx
服务器架构、分布式系统、负载均衡、微服务、高可用性
**分布式系统取代单体架构,以微服务实现高扩展性和灵活性。通过负载均衡技术增强性能,防止单点故障,结合冗余备份与故障切换保障高可用性,这种架构是支撑大规模在线业务的关键。**
55 3
|
14天前
|
存储 运维 物联网
【专栏】OceanBase 是一款先进的分布式数据库系统,以其分布式架构、高扩展性、高可用性和强一致性特点,应对大规模数据处理挑战
【4月更文挑战第29天】OceanBase 是一款先进的分布式数据库系统,以其分布式架构、高扩展性、高可用性和强一致性特点,应对大规模数据处理挑战。它支持混合负载,适用于金融、电商和物联网等领域,提供高性能、低成本的解决方案。尽管面临技术复杂性、数据迁移和性能优化等问题,通过合理策略可克服挑战。随着技术发展,OceanBase 在数字化时代将持续发挥关键作用。