redis+springboot集群定时任务锁解决方案

集群项目问题之一:定时任务的执行。即页面配置/项目启动等产生的定时任务如何在集群下维护?

本文仅供自己日后参考。

总结过程如下:

1.触发第一次启动线程时刻,计算下次执行时间放入redis;
2.执行中加锁并更新下次执行时间,到时间自动释放锁;
3.项目重新启动先取消已有线程,获取下次执行时间进行计算等待时间开启新的线程。
4.运行过程中修改定时任务相关配置,使用redis发布订阅重新开启定时任务。


代码示例:

  • 配置/修改周期

  •  redis订阅

  • 系统启动/重启加载

  •  redis工具类、订阅线程
    /**
     * 订阅消息
     *
     * @param subscriber 主题事件处理类
     * @param channel    发布的主题
     */
    public void subscribe(JedisPubSub subscriber, String... channel) {
        execute(new JedisActionNoResult() {
            @Override
            public void action(Jedis jedis) {
                jedis.subscribe(subscriber, channel);
            }
        });
    }

    /**
     * 发布消息
     *
     * @param channel 主题
     * @param message 消息
     * @return Long
     */
    public Long publish(String channel, String message) {
        return execute(new JedisAction<Long>() {
            @Override
            public Long action(Jedis jedis) {
                return jedis.publish(channel, message);
            }
        });
    }
    /**
     * 删除keys,如果指定key不存在,则直接忽略
     *
     * @param keys
     * @return 实际删除的数量
     */
    public Long del(final String... keys) {
        return execute(new JedisAction<Long>() {

            @Override
            public Long action(Jedis jedis) {
                return jedis.del(keys);
            }
        });
    }

    /**
     * 设定该Key持有指定的字符串Value,如果该Key已经存在,则覆盖其原有值。
     *
     * @param key
     * @param value
     */
    public Boolean set(final String key, final String value) {
        return execute(new JedisAction<Boolean>() {
            @Override
            public Boolean action(Jedis jedis) {
                String result = jedis.set(key, value);
                return LOCK_SUCCESS.equals(result);
            }
        });
    }
    /**
     * 原子性完成两个操作,一是设置该Key的值为指定字符串,同时设置该Key在Redis服务器中的存活时间(秒数)
     * 该命令主要应用于Redis被当做Cache服务器使用时
     *
     * @param key
     * @param seconds 设置的值的存活时间
     * @param value
     */
    public void setex(final String key, final int seconds, final String value) {
        execute(new JedisActionNoResult() {

            @Override
            public void action(Jedis jedis) {
                jedis.setex(key, seconds, value);
            }
        });
    }
    /**
     * 从redis中获取指定key对应的内容,如果key不存在, 返回null
     * 如果与该Key关联的Value不是string类型,Redis将返回错误信息,因为GET命令只能用于获取string Value
     *
     * @param key
     * @return
     */
    public String get(final String key) {
        return execute(new JedisAction<String>() {

            @Override
            public String action(Jedis jedis) {
                return jedis.get(key);
            }
        });
    }
/**
 * 系统通知消息监听线程
 */
@Component
public class SubscriberThread extends Thread {
    private static final Logger logger = LoggerFactory.getLogger(SubscriberThread.class);

    @Autowired
    private RedisClient redisClient;
    @Autowired
    private Subscriber subscriber;

    public SubscriberThread() {
        super("SubscriberThread");
    }

    @Override
    public void run() {
        try {

            logger.info("开始订阅 ...");
            redisClient.subscribe(subscriber, RaRedisKey.CHANNEL_SYSTEM_NOTICE, RaRedisKey.CHANNEL_BACKUP_ACTIFICIAL);
            logger.info("订阅成功!");
        } catch (Exception e) {
            logger.error("获取订阅消息异常", e);
        }
    }
}
  • 创建线程池
/**
 * @ClassName: DynamicThreadConstants class
 * @Description: 用于动态线程定时任务
 **/
@Configuration
public class DynamicThreadConstants {

    public static final Map<String, Future> DYNAMIC_THREAD_COLLECTION = new HashMap<>();

    public static ScheduledThreadPoolExecutor DYNAMIC_THREAD_POOL_EXECUTOR;

    @Value("${dynamic.thread.poolSize}")
    public void init(int poolSize){
        //初始化动态线程池
        synchronized (this) {
            if (null == DYNAMIC_THREAD_POOL_EXECUTOR) {
                DYNAMIC_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(poolSize);
            }
        }
    }

    //周期审计日志
    public static final String PERIOD_AUDIT_LOG_THREAD_NAME = "period_audit_log_thread";

}

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值