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

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

主要就是在高併發秒殺的場景下,不少人訪問時並無拿到鎖,因此直接跳過了。這樣就處理了多線程併發問題的同時也保證了服務器的性能的穩定。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;
    }


}

  

主要功能描述就是:

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

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

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

最後釋放分佈式鎖。

 

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

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

相關文章
相關標籤/搜索