多节点部署执行定时任务选举单一节点解决方案

基本思路:

利用缓存 + 随机数,定时任务首次执行时进行选举,并将选举结果保存下来,从而解决多节点定时任务重复跑的问题。任务分2个阶段,第一个阶段是投票阶段,第二个阶段是选举阶段。

主要代码如下

  /**

     * 首次选举后不再选举

     *

     * @param key

     * @param randomTime

     * @return

     * @author xx2018-9-26

     */

@Deprecated

    public boolean getExecFirstAth(String key, Integer randomTime) {

        boolean getExecAth = false;

        if (execFlag) {

            getExecAth = this.getExecAth(key, randomTime);

        }

        if (getExecAth) {

            retFlag = true;

        }

        execFlag = false;

        log.info("首次选举后不再选举 (true 抢到权限, false 没有抢到) -- :{}",retFlag);

        return retFlag;

    }

 

    /**

     * 权限分配方法,抢到机会返回true,否则返回false

     * 仅针对于 定时任务同时启动的场景

     *

     * @param key        建议命名规范:项目名_执行内容 如 xx_admin_importUser

     * @param randomTime 随机时间,越长重叠概率越小

     * @return

     * @author xx2018-9-26

     */

    public boolean getExecAth(String key, Integer randomTime) {

        //1 释放缓存

        cacheUtil.putHazelCastValue(key, "");

        //2 放入数据

        Random random = new Random();

        Integer time = random.nextInt(randomTime);

        log.debug("time -- > {}", time);

        MarketUtil.sleepThread(time);

        setIntoHazelcast(key, time);

        Set<Integer> getExecAth;

        //3 校验放入结果

        boolean breakFlag = true;

        while (breakFlag) {

            getExecAth = JsonUtil.jsonToObject(cacheUtil.gettHazelCastValue(key), HashSet.class);

            if (getExecAth.contains(time)) {

                breakFlag = false;

                log.debug("放入缓存成功!");

            } else {

                setIntoHazelcast(key, time);

            }

        }

        MarketUtil.sleepThread(randomTime);

        //4 取出比较

        getExecAth = JsonUtil.jsonToObject(cacheUtil.gettHazelCastValue(key), HashSet.class);

        log.debug("getExecAth -- > {}", getExecAth);

        boolean flag = false;

        if (!CollectionUtils.isEmpty(getExecAth)) {

            Integer max = getExecAth.stream().max(Integer::compare).get();

            flag = max.equals(time);

        }

        log.info("获取权限返回值(true 抢到权限, false 没有抢到) -- :{}", flag);

        return flag;

}

 

/**
 * 设置缓存值
 *
 * @param key
 * @param time
 * @author xx 2018-8-14
 */
private void setIntoHazelcast(String key, Integer time) {
    Set<Integer> execAth = new HashSet<>();
    execAth.add(time);
    Set<Integer> getExecAth = JsonUtil.jsonToObject(cacheUtil.gettHazelCastValue(key), HashSet.class);
    if (!CollectionUtils.isEmpty(getExecAth)) {
        log.debug("未取到,放入缓存!");
        getExecAth.addAll(execAth);
        cacheUtil.putHazelCastValue(key, JsonUtil.objectToJson(getExecAth));
    } else {
        cacheUtil.putHazelCastValue(key, JsonUtil.objectToJson(execAth));
    }
}

     调用方法:

    在定时任务的第一行加入如下代码即可

//定义一个选举机制
if (!xxxUtil.getExecFirstAth("xxxxx_xxxxxxxx", 60000)) {
    return;
}

说明: getExecFirstAth 方法是对getExecAth 方法的延申,即增加了首次选举结果保存功能,这样每次启动服务,只会选举一次,后面不用选举,保证日志在一个服务里,好定位问题。(失败概率的问题,可以忽略,非常非常非常小)

 但是后来发现这个方法存在漏洞,对于节点重启或者节点缩容/扩容的情况处理的不好,因此getExecFirstAth 方法,暂时废弃。还是每次都选举的方式稳定。

温馨提示:一定注意把缓存的命名空间,主键key值改成自己的,节点扩展时,要重启多有服务

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值