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

科技资讯 投稿 23700 0 评论

基于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 <T>
     * @throws Exception
     */
    public <T> void executeWithLock(String lockKey, ITask<T> 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> {
        T execute( throws Exception;
    }
}

调用示例

    @Resource
    MongoDBLock mongoDBLock;

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

原理

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

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

赞同 (122) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽