基於rdeis實現分佈式事務鎖

問題:多個線程同時進行購買商品操作,減庫存,增加記錄數據操作會出現庫存爲-1的情況。

1.使用同步鎖(synchronized 或 Lock),在單線程同步進行情況下不會出現庫存-1的情況

2.分佈式多個節點多線程同步進行請求會出現同步鎖無法解決的問題,依舊出現多減庫存的情況。

解決思路(一):redis分佈式事物鎖

思路:主要用到的redis函數是setnx(),這個應該是實現分佈式鎖最主要的函數。首先是將某一任務標識名(這裏用Lock:order作爲標識名的例子)作爲鍵存到redis裏,併爲其設個過期時間,如果是還有Lock:order請求過來,先是通過setnx()看看是否能將Lock:order插入到redis裏,可以的話就返回true,不可以就返回false

 

代碼:

 

package com.swxc.core.lock;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;

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

/**
 * @作者:huangliang
 * @時間:2019-9-27 19:25
 * @註釋:DistributedLock
 */
@Configuration
public class DistributedLock {


    private static  JedisPool jedisPool;

    @Bean
    public DistributedLock jpushServiceForStaff() {
        JedisPoolConfig config = new JedisPoolConfig();
        // 設置最大連接數
        config.setMaxTotal(200);
        // 設置最大空閒數
        config.setMaxIdle(8);
        // 設置最大等待時間
        config.setMaxWaitMillis(1000 * 100);
        // 在borrow一個jedis實例時,是否需要驗證,若爲true,則所有jedis實例均是可用的
        config.setTestOnBorrow(true);
        jedisPool = new JedisPool(config, "127.0.0.1", 6789, 3000,",./6789");
        return new DistributedLock(jedisPool);
    }


    public DistributedLock() {
    }

    public DistributedLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
    /**
     * 加鎖
     * @param locaName  鎖的key
     * @param acquireTimeout  獲取超時時間
     * @param timeout   鎖的超時時間
     * @return 鎖標識
     */
    public String lockWithTimeout(String locaName,
                                  long acquireTimeout, long timeout) {
        Jedis conn = null;
        String retIdentifier = null;
        try {
            // 獲取連接
            conn = jedisPool.getResource();
            // 隨機生成一個value
            String identifier = UUID.randomUUID().toString();
            // 鎖名,即key值
            String lockKey = "lock:" + locaName;
            // 超時時間,上鎖後超過此時間則自動釋放鎖
            int lockExpire = (int)(timeout / 1000);

            // 獲取鎖的超時時間,超過這個時間則放棄獲取鎖
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                if (conn.setnx(lockKey, identifier) == 1) {
                    conn.expire(lockKey, lockExpire);
                    // 返回value值,用於釋放鎖時間確認
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                // 返回-1代表key沒有設置超時時間,爲key設置一個超時時間
                if (conn.ttl(lockKey) == -1) {
                    conn.expire(lockKey, lockExpire);
                }

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retIdentifier;
    }

    /**
     * 釋放鎖
     * @param lockName 鎖的key
     * @param identifier    釋放鎖的標識
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            conn = jedisPool.getResource();
            while (true) {
                // 監視lock,準備開始事務
                conn.watch(lockKey);
                // 通過前面返回的value值判斷是不是該鎖,若是該鎖,則刪除,釋放鎖
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction transaction = conn.multi();
                    transaction.del(lockKey);
                    List<Object> results = transaction.exec();
                    if (results == null) {
                        continue;
                    }
                    retFlag = true;
                }
                conn.unwatch();
                break;
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retFlag;
    }
}

 

邏輯service調用:

@Resource
private DistributedLock distributedLock;

@Override
public Map test() {
    String indentifier = distributedLock.lockWithTimeout("orderLock", 5000, 10000);
    System.out.println("獲得了鎖");
    System.out.println("業務操作");
    distributedLock.releaseLock("resource", indentifier);
    return null;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章