1. 程式人生 > >Redis分散式鎖實現簡單秒殺功能

Redis分散式鎖實現簡單秒殺功能

這版秒殺只是解決瞬間訪問過高伺服器壓力過大,請求速度變慢,大大消耗伺服器效能的問題。

主要就是在高併發秒殺的場景下,很多人訪問時並沒有拿到鎖,所以直接跳過了。這樣就處理了多執行緒併發問題的同時也保證了伺服器的效能的穩定。

接下來我們使用redis的分散式鎖來進行枷鎖處理:

我們可以在進入下單的方法後將核心的方法加鎖,然後離開後進行解鎖

主要三步:

加鎖

核心方法

解鎖

首頁分散式加鎖解鎖工具類:

@Component
public class RedisLock {
    private static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 加鎖
     * @param key
     * @param value 當前事件+超時事件
     * @return
     */
    public boolean lock(String key,String value){
        //加鎖成功
        if (redisTemplate.opsForValue().setIfAbsent(key,value)){
            return true;
        }
        //假如currentValue=A先佔用了鎖  其他兩個執行緒的value都是B,保證其中一個執行緒拿到鎖
        String currentValue = redisTemplate.opsForValue().get(key);
        //鎖過期  防止出現死鎖
        if (!StringUtils.isEmpty(currentValue) &&
                Long.parseLong(currentValue) < System.currentTimeMillis()){
            //獲取上一步鎖的時間
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) &&
                    oldValue.equals(currentValue)){
                return true;
            }
        }
        return false;
    }

    /**
     * 解鎖
     * @param key
     * @param value
     */
    public void unlock(String key,String value){
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) &&
                    currentValue.equals(value)){
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        }catch (Exception e){
            logger.error("【redis分散式鎖】 解鎖異常,{}",e);
        }
    }
}

  

具體使用的邏輯程式碼功能:

@Service
public class SecKillService {
    private static Logger logger = LoggerFactory.getLogger(SecKillService.class);
    /** 超時時間 */
    private static final int TIMEOUT = 10000;

    @Autowired
    private RedisLock redisLock;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private RestTemplate restTemplate;


    /**
     * @Description: 秒殺商品介面
     * @param weddingExpoAppoint
     * @return JsonObject
     * @exception
     * @author mazhq
     * @date 2018/11/18 13:46
     */
    private JsonObject seckillProduct(long productId) {
        long time = System.currentTimeMillis() + TIMEOUT;
        String stockKey = RedisKeysManager.getWeddingExpoSeckillStockKey(productId);
        //加鎖
        String lockKey = "weddingExpo:seckill:"+productId;
        if (!redisLock.lock(lockKey,String.valueOf(time))){
            return BaseCode.retCode(100, "沒搶到,換個姿勢再來一遍");
        }

        String stockNumStr = redisClient.getStr(stockKey);
        int stockNum = 0;
        if(StringUtils.isNotBlank(stockNumStr)){
            stockNum = Integer.valueOf(stockNumStr);
        }
        JsonObject respJson = BaseCode.retCode(ResultCode.failure);
        if (stockNum == 0) {
            //庫存不足
            return BaseCode.retCode(100, "商品已經被搶光了,請留意下次活動");
        } else {

            try {
                String resp = doseckill(productId);
                if(null != resp){
                    respJson = new JsonObject(resp);
                    if(respJson.getInteger("retcode") == 0){
                        redisClient.increment(stockKey, -1);
                    }
                    Thread.sleep(100);
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
        //解鎖
        redisLock.unlock(lockKey, String.valueOf(time));
        return respJson;
    }


}

  

主要功能描述就是:

秒殺商品時候先加鎖,如果沒有獲取到鎖就釋放請求。 

加鎖後先進行庫存判斷如果不足釋放請求。

進行秒殺下單流程,如果成功庫存做減一操作。

最後釋放分散式鎖。

 

這樣簡單的分散式鎖處理秒殺功能的方法就搞定了。這種只是處理高併發下多個請求如果有人在秒殺後面的直接不需排隊直接釋放請求,解放伺服器壓力(處理流程時間較短,高併發下沒有排序要求)。

如果要根據請求時間進行排序,這個方式還需藉助佇列處理。