使用工具: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))); }