您当前的位置:首页 >> 资讯 >> 详情
全球今亮点!基于Mongodb分布式锁简单实现,解决定时任务并发执行问题
来源: 博客园      时间:2023-04-18 18:34:49


(资料图片仅供参考)

前言

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

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

All in one Code

先上代码

@Component@Slf4jpublic 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秒,并在执行完成之后会主动释放锁
标签: