1. 程式人生 > 實用技巧 >springboot redis 分散式鎖實現定時任務

springboot redis 分散式鎖實現定時任務

package com.rdj.distributelock;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class DistributeLockApplication {

    public static void main(String[] args) {
        SpringApplication.run(DistributeLockApplication.class, args);
    }

}



//service 
package com.rdj.distributelock.service;

import com.rdj.distributelock.lock.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

/**
 * redis 分散式鎖實現定時任務
 */
@Service
@Slf4j
public class SchedulerService {

    @Autowired
    private RedisTemplate redisTemplate;

    //模擬每隔5秒執行一次
    @Scheduled(cron = "*/5 * * * * ?")
    public void sendSms(){
        try(RedisLock redisLock = new RedisLock(redisTemplate,"autoSms",30)) {
            if (redisLock.getLock()) {
                //獲取到鎖,才執行定時任務
                log.info("傳送簡訊");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}



package com.rdj.distributelock.lock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

//AutoCloseable  jdk1.7 後增加的 自動關閉特性
@Slf4j
public class RedisLock implements AutoCloseable{
    private RedisTemplate redisTemplate;
    private String key;
    private String value;

    private int expireTime;//過期時間,單位秒

    public RedisLock(RedisTemplate redisTemplate, String key,int expireTime){
        this.redisTemplate = redisTemplate;
        this.key = key;
        this.expireTime = expireTime;
        this.value = UUID.randomUUID().toString();
    }

    /**
     * 獲取分散式鎖
     * @return
     */
    public boolean getLock(){
        RedisCallback<Boolean> redisCallback = connection -> {
            RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
            Expiration expiration = Expiration.seconds(expireTime);
            byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
            byte[] redisValue = redisTemplate.getKeySerializer().serialize(value);
            Boolean result = connection.set(redisKey, redisValue, expiration, setOption);
            return result;
        };
        boolean lock = (boolean) redisTemplate.execute(redisCallback);
        return lock;
    }

    public boolean unLock(){
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                "  return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "  return 0\n" +
                "end";
        RedisScript<Boolean> redisScript = RedisScript.of(script,Boolean.class);
        List<String> keys = Arrays.asList(key);
        Boolean res = (Boolean) redisTemplate.execute(redisScript, keys, value);
        log.info("釋放鎖的結果 {}",res);
        return res;
    }


    @Override
    public void close() throws Exception {
        unLock();
    }
}