使用工具:Apache an
測壓命令: ab -n 100 -c 100 http://www.baidu.com -n代表模擬100個請求,-c代表模擬100個並發,相當於100個人同時訪問
ab -t 60 -c 100 http://www.baidu.com 60秒100個並發,不斷發送請求
並發處理:
1.加synchronized鎖單線程處理、缺點: 無法做到細粒度控制,處理速度也會很慢 只適合單點的情況
2.redis分布式鎖:
可以支撐每秒10多萬的並發,支持分布式,可以更細粒的控制代碼(多台機器上多個線程對一個數據進行操作的互斥)
SETNX key value
將key設置值為value,如果key不存在,這種情況下等同於SET命令,當key存在時,什么也不做
GETSET key value
自動將key對應到value並且返回原來key和對應的value,如果key存在但是對應的value不是字符串,就返回錯誤
DEMO演示:
加鎖處理方法:
@Component
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate stringRedisTemplate;
//加鎖
/*
* @param key id
* @param value 當前時間+超時時間
*
* */
public boolean lock(String key,String value){
if (stringRedisTemplate.opsForValue().setIfAbsent(key,value)){
return true;//加鎖成功就返回true
}
//不加下面這個可能出現死鎖情況
//代碼value加了過期時間* @param value 當前時間+超時時間
//獲取上一個鎖的時間,並判斷是否小於當前時間,小於就下一步判斷,就返回true加鎖成功
//currentValue=A 這兩個線程的value都是B 其中一個線程拿到鎖
String currentValue=stringRedisTemplate.opsForValue().get(key);
//如果鎖過期
if (!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue)<System.currentTimeMillis()){//存儲時間要小於當前時間
//出現死鎖的另一種情況,當多個線程進來后都沒有返回true,接着往下執行,執行代碼有先后,而if判斷里只有一個線程才能滿足條件
//oldValue=currentValue
//多個線程進來后只有其中一個線程能拿到鎖(即oldValue=currentValue),其他的返回false
//獲取上一個鎖的時間
String oldValue=stringRedisTemplate.opsForValue().getAndSet(key,value);
if (!StringUtils.isEmpty(oldValue)&& oldValue.equals(currentValue)){//上一個時間不為空,並且等於當前時間
return true;
}
}
return false;//失敗返回false
}
//解鎖
public void unlock(String key,String value){//執行刪除可能出現異常需要捕獲
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {//如果不為空,就刪除鎖
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
}catch (Exception e){
log.error("[redis分布式鎖] 解鎖",e);
}
}
}
//秒殺demo
pprivate static final int TIMEOUT=10*1000;//超時時間設置為10s
@Autowrite
private RedisLock redisLock;
public void method(String id){
//加鎖-死鎖出現:即在加鎖后運行程序出現意外報了異常,而此時還沒調用解鎖方法
//那么在下一個線程調用加鎖方法是就不能set,直接返回fale,然后一直停留在加鎖失敗狀態 這就出現了死鎖
long time=System.currentTimeMillis()+TIMEOUT;
//如果加鎖不成功就拋出異常
if(!redisLock.lock.get(id,String.valueof(time))){
throw new WechatSellException(101,"哎喲喂,人也太多了,換個姿勢再試試");
}
//加鎖成功就實現業務代碼處理
//1.查詢該商品庫存,為0表示活動結束
//2.下單
3.扣庫存
//解鎖
redisLock.unlock(id,String.valueof(time)));
}
