這版秒殺只是解決瞬間訪問過高服務器壓力過大,請求速度變慢,大大消耗服務器性能的問題。
主要就是在高並發秒殺的場景下,很多人訪問時並沒有拿到鎖,所以直接跳過了。這樣就處理了多線程並發問題的同時也保證了服務器的性能的穩定。
接下來我們使用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;
}
}
主要功能描述就是:
秒殺商品時候先加鎖,如果沒有獲取到鎖就釋放請求。
加鎖后先進行庫存判斷如果不足釋放請求。
進行秒殺下單流程,如果成功庫存做減一操作。
最后釋放分布式鎖。
這樣簡單的分布式鎖處理秒殺功能的方法就搞定了。這種只是處理高並發下多個請求如果有人在秒殺后面的直接不需排隊直接釋放請求,解放服務器壓力(處理流程時間較短,高並發下沒有排序要求)。
如果要根據請求時間進行排序,這個方式還需借助隊列處理。
