• 基于Mongodb分布式锁简单实现,解决定时任务并发执行问题


    前言

    我们日常开发过程,会有一些定时任务的代码来统计一些系统运行数据,但是我们应用有需要部署多个实例,传统的通过配置文件来控制定时任务是否启动又太过繁琐,而且还经常出错,导致一些异常数据的产生

    网上有很多分布式锁的实现方案,基于redis、zk、等有很多,但是我的就是一个用了mysql和mongo的小应用,不准备引入其他三方中间件来解决这个问题,撸一个简单的分布式锁来解决定时任务并发执行的问题,加锁操作的原子性和防死锁也都要支持,这里我使用mongodb写了AllInOne的工具类

    All in one Code

    先上代码

    @Component
    @Slf4j
    public class MongoDBLock {
    
        private static final int DEFAULT_LOCK_TIMEOUT = 30;//锁的默认超时时间,单位秒
    
        private MongoTemplate mongoTemplate;
        private int lockTimeout;
    
        public MongoDBLock(MongoTemplate mongoTemplate) {
            this.mongoTemplate = mongoTemplate;
            this.lockTimeout = DEFAULT_LOCK_TIMEOUT;
        }
    
        /**
         * 尝试获取分布式锁
         *
         * @param lockKey 锁的key
         * @return true:获取锁成功,false:获取锁失败
         */
        private boolean acquireLock(String lockKey) {
            LockDocument document = new LockDocument();
            document.setId(lockKey);
            document.setExpireAt(Instant.ofEpochMilli(Instant.now().toEpochMilli() + lockTimeout * 1000));
            try {
                mongoTemplate.insert(document);
                return true;
            } catch (Exception e) {
    
            }
            return false;
        }
    
        /**
         * 释放分布式锁
         *
         * @param lockKey 锁的key
         */
        private void releaseLock(String lockKey) {
            Query query = new Query(Criteria.where("key").is(lockKey));
            mongoTemplate.remove(query, LockDocument.class);
            log.info("程序执行成功,释放分布式锁,lockKey:{}",lockKey);
        }
    
        /**
         * 分布式锁入口方法,参数lockName为锁的名称,lockKey为需要加锁的key,执行完成后自动释放锁
         *
         * @param lockKey
         * @param task
         * @param 
         * @throws Exception
         */
        public  void executeWithLock(String lockKey, ITask task) throws Exception {
            boolean locked = acquireLock(lockKey);
            if (locked) {
                log.info("获取分布式锁成功,lockKey:{}",lockKey);
                try {
                    task.execute();
                } finally {
                    releaseLock(lockKey);
                }
            } else {
                log.warn("获取分布式锁失败,lockKey:{}", lockKey);
                throw new AppException("获取分布式锁失败!");
            }
        }
    
        @Data
        @Document(collection = "lock_collection")
        static class LockDocument {
            @Id
            private String id;
            @Indexed(expireAfterSeconds = DEFAULT_LOCK_TIMEOUT)
            private Instant expireAt;
        }
    
        @FunctionalInterface
        public interface ITask {
            T execute() throws Exception;
        }
    }
    

    调用示例

        @Resource
        MongoDBLock mongoDBLock;
    
        mongoDBLock.executeWithLock("key", () -> {
            // do some thing
            return null;
        });
    

    原理

    • 使用key作为主键,利用mongodb的insert原子性保障LockDocument不会重复插入
    • LockDocument中expireAt字段利用的mongodb索引过期机制,解决死锁问题,这里设置超时时间是30秒,并在执行完成之后会主动释放锁

    __EOF__

  • 本文作者: Dandelion Rhapsody
  • 本文链接: https://www.cnblogs.com/surging-dandelion/p/17330389.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    【k8s源码篇之Informer篇2】理解Informer内部的运行逻辑
    [附源码]java毕业设计基于的图书馆管理系统
    Codeforces Round #834 (Div. 3) A-G
    AVL树的实现及原理
    STM学习记录(四)———中断及NVIC
    @Scheduled定时器
    QT:制作图片浏览器
    Redis面试问题汇总
    postgresql idle in transaction 进程如何杀死
    【推荐】web3.0的发展与研究资料合集
  • 原文地址:https://www.cnblogs.com/surging-dandelion/p/17330389.html