java分布式系统定时任务,如何保证多台服务只执行一次

时间:2021-03-29 08:02:42


思路:让定时任务执行时,随机睡几秒钟,首先执行的服务器更新job开关,执行完毕后关闭开关



一、代码

	private static String serverIp = null;

    static {
        //获取服务器的IP地址,便于后续追踪
        try {
            InetAddress address = InetAddress.getLocalHost();
            serverIp = address.getHostAddress();
        } catch (Exception e) {
            logger.error("获取服务器IP地址有误!!");
            e.printStackTrace();
        }
    }

    /**
     * 每日凌晨1点执行
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void xxxSchedule(){
        logger.info("job start");
        String jobName = "xxx";
        long startTime = new Date().getTime();
        try {
            if (canExecute(jobName)){
                jobMapper.xxxSchedule();
                jobMapper.updateJobStatus(jobName,serverIp,"off");
                logger.info(jobName+"执行完成,耗时:"+(new Date().getTime()-startTime)+"毫秒!");
            }
        } catch (Exception e) { 
            e.printStackTrace(); 
        }
    }


    private Boolean canExecute(String jobName) throws Exception {
        int max = 10000;
        int min = (int) Math.round(Math.random()*8000);
        long sleepTime = Math.round(Math.random()*(max-min));
        logger.info(jobName+"睡了:"+ sleepTime + "毫秒");
        Thread.sleep(sleepTime);

        if (jobMapper.getJobOff(jobName) == 1){
            jobMapper.updateJobStatus(jobName,serverIp,"on");
            return true;
        }
        logger.info(jobName+"已被其他服务器执行");
        return false;
    }


二、数据库表

CREATE TABLE `job_manager` (
  `job_name` varchar(50) NOT NULL PRIMARY KEY COMMENT '名称',
  `job_desc` varchar(50)  COMMENT 'job描述',
  `server_ip` varchar(50) NOT NULL COMMENT '服务器ip',
  `on_off` varchar(10) DEFAULT 'off' COMMENT '开关(on:正在执行,off:执行完毕)',
  `update_date` datetime NOT NULL COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='job管理' ROW_FORMAT=DYNAMIC;


INSERT INTO `job_manager`
VALUES
('xxx','更新xxx的job','0.0.0.0','off',now());

三、查询或改状态的sql

如下sql中:on_off = 'off' AND update_date < curdate(),是可执行job的标志

<select id="getJobOff" parameterType="string" resultType="int">
        SELECT count(1) FROM job_manager
        WHERE job_name = #{jobName} AND on_off = 'off' AND update_date < curdate()
    </select>

    <update id="updateJobStatus" parameterType="map">
        UPDATE job_manager
        SET on_off=#{status},server_ip=#{serverIp},update_date=now()
        WHERE job_name = #{jobName}
    </update>