Springboot multi-instance start timing task

1. Bring your own springboot project

2. Start instance one, configure 8080 (default port)

3. Configure 8081 instance 2, specify the port to start

4. Timing tasks

@[email protected] class ScheduleService {     @Scheduled(cron = "0/10 * * * * ? ")    public void sayHello(){        log.info("当前线程{},定时任务执行,{}", Thread.currentThread().getName(), "hello world");    } }

5. View 8080, you can see that the timed task of the 8080 instance has been executed

6. View 8081, timing tasks are also executed

7. From the results, both instance 1 and instance 2 are executing timed tasks, but you need to pay attention: if the timed task is performing operations such as inventory, you need to pay attention to whether you need to execute both instances or only need one instance to execute.

8. Use redis distributed lock, grab the lock and then execute it, the expiration time of the key is 30s

package com.frank.redis.util; import org.apache.commons.lang.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component; import java.time.Duration;import java.util.concurrent.TimeUnit; /** * @author 小石潭记 * @date 2021/6/8 21:13 * @Description: ${todo} */@Componentpublic class RedisLockUtil {    @Autowired    private StringRedisTemplate stringRedisTemplate;     /**     * Redis加锁的操作     *     * @param key     * @param value     * @return     */    public Boolean tryLock(String key, String value) {        if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {            // 设置key 30s过期            stringRedisTemplate.expire(key, 30, TimeUnit.SECONDS);            return true;        }        String currentValue = stringRedisTemplate.opsForValue().get(key);        if (StringUtils.isNotEmpty(currentValue) && Long.valueOf(currentValue) < System.currentTimeMillis()) {            //获取上一个锁的时间 如果高并发的情况可能会出现已经被修改的问题所以多一次判断保证线程的安全            String oldValue = stringRedisTemplate.opsForValue().getAndSet(key, value);            if (StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)) {                return true;            }        }        return false;    }      /**     * Redis解锁的操作     *     * @param key     * @param value     */    public void unlock(String key, String value) {        String currentValue = stringRedisTemplate.opsForValue().get(key);        try {            if (StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)) {                stringRedisTemplate.opsForValue().getOperations().delete(key);            }        } catch (Exception e) {        }    }} 

9. Timing task modification

package com.frank.asynService.service; import com.frank.redis.util.RedisLockUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Service; /** * @author 小石潭记 * @date 2020/10/27 11:47 * @Description: ${todo} */@[email protected] class ScheduleService {     @Autowired    private RedisLockUtil redisLock;     @Scheduled(cron = "0/10 * * * * ? ")    public void sayHello(){        String key = "dec_store_lock";        long time = System.currentTimeMillis();        try {            //如果加锁失败            if (!redisLock.tryLock(key, String.valueOf(time))) {                return;            }            // 执行真正的业务操作            log.info("当前线程{},定时任务执行,{}", Thread.currentThread().getName(), "hello world");        } catch (Exception e) {            //解锁 异常            redisLock.unlock(key, String.valueOf(time));            return;        }     } }

10. View the execution of the timing task, it will be executed only after the lock is grabbed, and only one instance has executed the timing task in the 21.30.20 period