分布式系统中定时任务保证相同任务只有一个执行

原文链接 https://zhuanlan.zhihu.com/p/97068262    https://blog.csdn.net/J_Shine/article/details/80406743

思路:

1.使用redis分布式锁,为定时任务唯一指定的key加锁,并设置锁超时时间。当触发定时任务时,通过setNX(key,value)方法为唯一的key加锁,如果当前key不存在,将放入缓存,并返回true,通过expire(key,second)设置锁超时时间,结束后跳出执行定时任务方法。第二台服务任务进入时,设置锁的时候发现该锁已存在于缓存,并返回false,不跳转到执行定时任务方法。

检测是否成功执行:setNX(key,value) 以定时任务唯一指定id为key,value开始时间时间戳,如八点开始执行定时任务,然后八点半设置一个检测任务,获取定时任务的的时间戳并做差,若差值大于30分钟,则说明这个任务八点未执行,释放锁。小于30分钟则不做处理。

package com.ayx..redis.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


@Component
public class RedisUtils {

    private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);

    @Autowired
    private RedisTemplate redisTemplate;
	 /**
     * 上锁
     * 将键值对设定一个指定的时间timeout.
     *
     * @param key
     * @param timeout 键值对缓存的时间,单位是秒
     * @return 设置成功返回true,否则返回false
     */
    public  boolean tryLock(String key, Object value, long timeout) {
        //底层原理就是Redis的setnx方法
        boolean isSuccess = redisTemplate.opsForValue().setIfAbsent(key, value);
        if (isSuccess) {
			//设置分布式锁的过期时间
            redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
        return isSuccess;
    }
package com.ayx.test;

import com.xjb.redis.utils.RedisUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.UnknownHostException;

@Component
public class RedisLockTest {

    private static Logger logger = LoggerFactory.getLogger(RedisLockTest.class);
    @Autowired
    RedisUtils redisUtils;

    String redisKey = "demo-RedisLockTest-isRun";

    //单位为秒  默认三分钟
    private long redis_default_expire_time = 60 * 3;


     @Scheduled(cron = "0 */1 * * * ?")
    public void init() throws InterruptedException {


        //-------------上分布式锁开始-----------------

        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        //获取本机ip
        String ip = addr.getHostAddress();
        //默认上锁时间为五小时
        //此key存放的值为任务执行的ip,
        // redis_default_expire_time 不能设置为永久,避免死锁
        boolean lock = redisUtils.tryLock(redisKey, ip, redis_default_expire_time);
        logger.info("============本次聚类定时任务开始==============");
        if (lock) {
            logger.info("============获得分布式锁成功=======================");
            //TODO 开始执行任务 执行结束后需要释放锁
            run();
            //释放锁
            redisUtils.del(redisKey);
            logger.info("============释放分布式锁成功=======================");

        } else {
            logger.info("============获得分布式锁失败=======================");
            ip = (String) redisUtils.get(redisKey);
            logger.info("============{}机器上占用分布式锁,聚类任务正在执行=======================", ip);
            logger.info("============本次聚类定时任务结束==============");
            return;
        }
    }

    public void run() throws InterruptedException {

        logger.info("执行中");
        Thread.sleep(1000 * 60 * 2);
        logger.info("执行结束");

    }
}

2.对于每一个定时任务job都有一个唯一id,当发现改job处于运行状态时,其他机器都不会去调度这个job。

quartz的分布式解决方案
quartz介绍
Quartz是一个开源的作业调度包,能够运行在几乎任何java项目中,小到单机应用,大到电商系统。Quartz能够创建很容易的调度,也可以创建十个、百个、千个、甚至万个任务的复杂调度。Quartz将任务定义成java组件,能够执行几乎任何你定义的事情。Quartz也支持许多企业级特性,比如JTA和集群。Quartz的核心是Scheduler、Job、Trigger。Job负责定义需要执行的任务,Trigger负责调度策略,Scheduler负责将二者组合。

quartz的分布式解决方案
启用quartz的分布式任务调度处理,只需如下两个步骤,程序无需修改

在数据库中创建quartz需要的表。这些表由quartz自己维护,用于保存数据和维护程序运行状态,这些表对于程序员而言是透明的,不需要关注,只需创建好表即可。
在工程的src下增加quartz.properties配置文件,进行集群相关操作的配置。

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值