1. 程式人生 > >spring boot專案中redis分散式鎖實現 程式碼模板

spring boot專案中redis分散式鎖實現 程式碼模板

1,在application.properties中配置redis主機:

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=XXX
2,新增redis配置檔案:

cache/RadisLock.java

import org.springframework.data.redis.core.RedisTemplate;

public class RedisLock {

    private RedisTemplate<String, String> redisTemplate;
    /**
     * 重試時間
     */
    private static final int DEFAULT_ACQUIRY_RETRY_MILLIS = 100;
    /**
     * 鎖的字尾
     */
    private static final String LOCK_SUFFIX = "_redis_lock";
    /**
     * 鎖的key
     */
    private String lockKey;
    /**
     * 鎖超時時間,防止執行緒在入鎖以後,防止阻塞後面的執行緒無法獲取鎖
     */
    private int expireMsecs = 60 * 1000;
    /**
     * 執行緒獲取鎖的等待時間
     */
    private int timeoutMsecs = 10 * 1000;
    /**
     * 是否鎖定標誌
     */
    private volatile boolean locked = false;

    /**
     * 構造器
     * 
     * @param redisTemplate
     * @param lockKey 鎖的key
     */
    public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey){
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey + LOCK_SUFFIX;
    }

    /**
     * 構造器
     * 
     * @param redisTemplate
     * @param lockKey 鎖的key
     * @param timeoutMsecs 獲取鎖的超時時間
     */
    public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey, int timeoutMsecs){
        this(redisTemplate, lockKey);
        this.timeoutMsecs = timeoutMsecs;
    }

    /**
     * 構造器
     * 
     * @param redisTemplate
     * @param lockKey 鎖的key
     * @param timeoutMsecs 獲取鎖的超時時間
     * @param expireMsecs 鎖的有效期
     */
    public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey, int timeoutMsecs, int expireMsecs){
        this(redisTemplate, lockKey, timeoutMsecs);
        this.expireMsecs = expireMsecs;
    }

    public String getLockKey() {
        return lockKey;
    }

    /**
     * 封裝和jedis方法
     * 
     * @param key
     * @return
     */
    private String get(final String key) {
        Object obj = redisTemplate.opsForValue().get(key);
        return obj != null ? obj.toString() : null;
    }

    /**
     * 封裝和jedis方法
     * 
     * @param key
     * @param value
     * @return
     */
    private boolean setNX(final String key, final String value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /**
     * 封裝和jedis方法
     * 
     * @param key
     * @param value
     * @return
     */
    private String getSet(final String key, final String value) {
        Object obj = redisTemplate.opsForValue().getAndSet(key, value);
        return obj != null ? (String) obj : null;
    }

    /**
     * 獲取鎖
     * 
     * @return 獲取鎖成功返回ture,超時返回false
     * @throws InterruptedException
     */
    public boolean lock() throws InterruptedException {
        int timeout = timeoutMsecs;
        while (timeout >= 0) {
            long expires = System.currentTimeMillis() + expireMsecs + 1;
            String expiresStr = String.valueOf(expires); // 鎖到期時間
            if (this.setNX(lockKey, expiresStr)) {
                locked = true;
                return true;
            }
            // redis裡key的時間
            String currentValue = this.get(lockKey);
            // 判斷鎖是否已經過期,過期則重新設定並獲取
            if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {
                // 設定鎖並返回舊值
                String oldValue = this.getSet(lockKey, expiresStr);
                // 比較鎖的時間,如果不一致則可能是其他鎖已經修改了值並獲取
                if (oldValue != null && oldValue.equals(currentValue)) {
                    locked = true;
                    return true;
                }
            }
            timeout -= DEFAULT_ACQUIRY_RETRY_MILLIS;
            // 延時
            Thread.sleep(DEFAULT_ACQUIRY_RETRY_MILLIS);
        }
        return false;
    }

    /**
     * 釋放獲取到的鎖
     */
    public void unlock() {
        if (locked) {
            redisTemplate.delete(lockKey);
            locked = false;
        }
    }

}

3,在程式碼中使用redis:

    /**
     * 新建sdk組織
     *
     * @param request
     * @param result
     */
    @RequestMapping(value = "/sdk", method = RequestMethod.POST)
    public ResponseResult createBizSDKGroups(HttpServletRequest request,
            @RequestBody @Validated final SdkGroupAO sdkGroupAO, BindingResult result) {
        ResultDelegate delegate = new ResultDelegate() {

            @Override
            public Object getResultObject() {
                RedisLock redisLock = new RedisLock(redisTemplate, "create#sdkgroup#" + sessionUser);
                try {
                    if (redisLock.lock()) {
                        try {
                            bizGroupService.createBizSDKGroup(sessionUser, sdkGroupAO);
                        } finally {
                            redisLock.unlock();
                        }
                    } else {
                        return "請不要多次點選";
                    }
                } catch (InterruptedException e) {
                    return "建立失敗";
                }
                return 1;
            }
        };
        return getResponseResult(request, delegate, result);
    }