集群部署项-关于SpringTask的坑(分布式锁)

1 通过SpringTask执行定时任务

1.1 创建定时任务

我们要在下面的代码中,实现每5秒钟执行一个打印信息的任务。

package com.yings.part7.day85.task;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class PrintTask {
    // 任务默认同步执行的
    @Async  // 表示异步执行的注解
    @Scheduled(cron = "5 * * * * ?")// 每分钟的第5秒触发定时任务
    public void printInfo() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
        System.out.println(sdf.format(new Date()) + ": print info test");
    }
}

1.2 启动同一个项目的多个实例

首先我们可以通过“Edit Configurations”,增加一个运行实例的配置。
在这里插入图片描述
接着启动项目的多个实例即可,如下图所示:

在这里插入图片描述

1.3 查看执行结果

然后我们就可以查看上面代码的执行结果了,如下图

在这里插入图片描述 在这里插入图片描述

通过对比执行结果,我们会发现,有两个实例"同时"执行了任务。实际开发中,我们的项目经常会进行集群部署,但是,如果我们通过Spring Task来执行定时任务,只需要一个实例执行任务即可。
那么,如果我们不想有多个实例同时执行任务,需要怎么办呢?
一般我们可以借助分布式锁对任务进行加锁,防止多个实例同时执行任务。但这样做,真的可以吗?

2 定时任务中使用分布式锁

2.1通过Redisson对任务加锁

加锁后的代码如下:

package com.yings.part7.day85.task;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Component
public class PrintTask {
    // 注入RedissonClient 对象
    @Resource
    private RedissonClient redissonClient;

    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void printInfo2() {
        // 获取锁对象,参数表示redis中key值
        RLock taskLock = redissonClient.getLock("taskLock");

        try {
            // 尝试加锁,
            // 第一个参数,表示加锁的重试时间,如果锁被占用,在指定时间内尝试加锁
            // 第二个参数,表示锁过期时间
            // 返回true/false
            // 本例中,第一个参数是0,表示如果没有加上锁,直接返回false
            if (taskLock.tryLock(0, 10, TimeUnit.SECONDS)) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                System.out.println(sdf.format(new Date()) + ": print info test");
                // Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (taskLock.isLocked()) {
                // 判断是否当前线程拥有该锁
                if (taskLock.isHeldByCurrentThread()) {
                    // 释放锁
                    taskLock.unlock();
                }
            }
        }
    }
}

本例不讨论Redisson的使用。

1.2 执行结果

在这里插入图片描述
在这里插入图片描述

通过上图的执行结果,我们发现,虽然使用了分布式锁,但在某些时间点,比如“17:38:15”,两个实例还是“同时”执行了相同的任务。
难道分布式锁没有起作用?当然不是,真相其实是这个样子的…
我们的任务执行地太快了!!!
Day85Application2这个实例,在17:38:15.008时对任务加锁并且执行,任务很快执行完毕。Day85Application这个实例,在17:38:15.029执行任务,加锁时,发现锁没被占用,于是又执行了一次任务。
通过上面的分析,我们会发现,如果任务执行时间很短,在一秒内多个实例可以多次加锁并执行任务,于是就出现了在同一秒内,任务“同时”执行的现象。

2.3 解决方案

要想解决上述问题,我们可以把代码稍微改一下:
在这里插入图片描述
修改方案如上图代码所示,让任务强行休眠1秒。再次执行,你就发现,现在已经不会出现同一秒内任务“同时”执行的现象了。
但是,这种做法实在是有辱斯文。如果项目需要进行集群部署,我们在使用定时任务时,最好还是使用第三方的分布式任务框架,比如quartz、xxl-job等。

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yinying293

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值