基于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秒,并在执行完成之后会主动释放锁

原文链接:https://www.cnblogs.com/surging-dandelion/p/17330389.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:基于Mongodb分布式锁简单实现,解决定时任务并发执行问题 - Python技术站

(0)
上一篇 2023年4月19日
下一篇 2023年4月19日

相关文章

  • 基于Java方式实现数据同步

    前言 在本文中,我们将介绍如何使用Java实现数据同步的基本原理以及如何实际地应用它。本文将包含两个步骤:首先我们将使用Java编写多线程程序从一个数据库中读取数据,并将其插入到另一个数据库中,以实现数据同步的基本原理。然后我们将使用示例说明如何使用这种方式实现两个不同数据库之间的数据同步。 数据同步的基本原理 实现数据同步的基本原理是通过编写一个程序来自动…

    Java 2023年5月18日
    00
  • Spring常用配置及解析类说明

    下面是“Spring常用配置及解析类说明”的详细攻略。 1. Spring常用配置 1.1 XML配置 Spring框架最初是以XML配置为主的,XML配置的方式包括声明bean和对bean进行依赖注入两个方面。 1.1.1 声明bean 在XML配置文件中,声明bean的方式如下: <bean id="beanId" class=…

    Java 2023年5月19日
    00
  • JavaWeb Listener 利用Session统计在线人数

    下面我将详细讲解“JavaWeb Listener 利用Session统计在线人数”的完整攻略。 什么是Listener Listener 是 JavaWeb 中的一种组件,用于监听某一种事件的发生,并在适当的时候做出反应。常用的一些监听器有 ServletContextListener、HttpSessionListener、ServletRequestL…

    Java 2023年6月15日
    00
  • jsp编程去除空白行的方法

    下面是“jsp编程去除空白行的方法”的完整攻略: 1. 使用JSTL标签库 JSP的JSTL标签库中提供了c:out标签,可以将JSP页面中的换行、空格等无效字符去掉,实现去除空白行的效果。具体操作步骤如下: 在JSP页面中引入JSTL标签库 <%@ taglib prefix="c" uri="http://java.s…

    Java 2023年6月15日
    00
  • 用Maven打成可执行jar,包含maven依赖,本地依赖的操作

    Maven 是一个强大的项目管理工具,可以帮助开发者自动下载和安装项目所需的依赖,并且可以将项目打包成可执行的 jar 文件。下面是用 Maven 将项目打包成可执行的 jar,同时包含 Maven 依赖和本地依赖的操作攻略。 1. 在 pom.xml 文件中配置插件 在 Maven 的 pom.xml 文件中,可以通过配置插件的方式来打包成可执行的 jar…

    Java 2023年5月20日
    00
  • 什么是Java Agent?

    Java Agent是一种Java应用程序的附加组件,它可以通过Java虚拟机的自定义类加载器来加载并执行,从而在应用程序生命周期内提供额外的功能和服务。Java Agent常见的应用场景包括:性能监测、应用程序调试、代码覆盖率和行为分析、安全检测、依赖项注入等。本文将介绍Java Agent的完整使用攻略,并给出两个实际示例说明。 一、Java Agent…

    Java 2023年5月11日
    00
  • 浅谈MyBatis 如何执行一条 SQL语句

    MyBatis 是一个优秀的持久化框架,其底层也是通过 JDBC 实现对数据库的操作。下面,我们就来详细讲解一下 MyBatis 如何执行一条 SQL 语句的完整攻略。 1. 读取 XML 配置文件 首先,MyBatis 会读取类路径下的 mybatis-config.xml(或者其他指定的配置文件)文件,该文件中包含了 MyBatis 的全局配置信息,其中…

    Java 2023年6月1日
    00
  • 解决jmap命令打印JVM堆信息异常的问题

    以下是解决 “jmap命令打印JVM堆信息异常的问题” 的攻略: 问题描述 当我们使用 jmap 命令打印JVM堆信息时,可能会遇到如下异常: Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can’t attach to the process: ptrace(…

    Java 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部