學習自:位址
1.主要流程
1.1資料庫:

1.2 環境
window下:Zookeeper,Redis,rabbitmq-server。jdk1.8以上。
1.3 介紹
這裡隻做秒殺部分功能,其他功能不會涉及。項目運作後可通路秒殺商品頁面
當使用者沒登陸,點選詳情會跳轉到登陸頁面。
使用者登陸後可以檢視商品的詳情并進行搶購。
注意,使用者對于一件商品隻能搶購一次,進行第二次搶購時會被拒絕。當使用者搶購成功時會異步發送一封郵件給使用者。
主要邏輯就是以上。接下來看代碼
1.4 項目結構,api封裝一些枚舉和傳回值,model主要是實體類和sql映射檔案,service實作業務邏輯代碼。
1.5 顯示秒殺商品到頁面以及使用者的操作使用的還是MVC模式,不細講。主要看如實作高并發下的秒殺。
要細述的話,東西太多,如果想深入了解,可點選上面的連結。
基本的秒殺邏輯如下,判斷使用者是否已經搶購過該商品,如果沒有則查詢待秒殺商品詳情,判斷該商品是否可以别秒殺,判斷依據為庫存是否足夠
如果符合條件,則該商品庫存減1,接着,再一次判斷扣減是否成功,如果扣減成功則生成秒殺成功的訂單,同時通知使用者秒殺成功的資訊。
public Boolean killItem(Integer killId, Integer userId) throws Exception {
Boolean result=false;
//TODO:判斷目前使用者是否已經搶購過目前商品
if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
//TODO:查詢待秒殺商品詳情
ItemKill itemKill=itemKillMapper.selectById(killId);
//TODO:判斷是否可以被秒殺canKill=1?
if (itemKill!=null && 1==itemKill.getCanKill() ){
//TODO:扣減庫存-減一
int res=itemKillMapper.updateKillItem(killId);
//TODO:扣減是否成功?是-生成秒殺成功的訂單,同時通知使用者秒殺成功的消息
if (res>0){
commonRecordKillSuccessInfo(itemKill,userId);
result=true;
}
}
}else{
throw new Exception("您已經搶購過該商品了!");
}
return result;
}
代碼優化1:使用redis的分布式鎖,使用目前秒殺商品的id和目前使用者的id組成一個key,使用StringBuffer拼接,使用雪花算法生成一個value,存進redis中。
Boolean cacheRes=valueOperations.setIfAbsent(key,value);目前鎖唯一,一次隻能放一個使用者進行操作,操作結束後,釋放該鎖。
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 商品秒殺核心業務邏輯的處理-redis的分布式鎖
* @param killId
* @param userId
* @return
* @throws Exception
*/
@Override
public Boolean killItemV3(Integer killId, Integer userId) throws Exception {
Boolean result=false;
if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
//TODO:借助Redis的原子操作實作分布式鎖-對共享操作-資源進行控制
ValueOperations valueOperations=stringRedisTemplate.opsForValue();
final String key=new StringBuffer().append(killId).append(userId).append("-RedisLock").toString();
final String value=RandomUtil.generateOrderCode();
Boolean cacheRes=valueOperations.setIfAbsent(key,value); //luna腳本提供“分布式鎖服務”,就可以寫在一起
//TOOD:redis部署節點當機了
if (cacheRes){
stringRedisTemplate.expire(key,30, TimeUnit.SECONDS);
try {
ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
int res=itemKillMapper.updateKillItemV2(killId);
if (res>0){
commonRecordKillSuccessInfo(itemKill,userId);
result=true;
}
}
}catch (Exception e){
throw new Exception("還沒到搶購日期、已過了搶購時間或已被搶購完畢!");
}finally {
if (value.equals(valueOperations.get(key).toString())){
stringRedisTemplate.delete(key);
}
}
}
}else{
throw new Exception("Redis-您已經搶購過該商品了!");
}
return result;
}
代碼優化2:将 Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS); 每隔30秒判斷目前使用者是否逾時寫在了鎖外面,不會因為一次卡頓而影響整個程式。
@Autowired
private RedissonClient redissonClient;
/**
* 商品秒殺核心業務邏輯的處理-redisson的分布式鎖
* @param killId
* @param userId
* @return
* @throws Exception
*/
@Override
public Boolean killItemV4(Integer killId, Integer userId) throws Exception {
Boolean result=false;
final String lockKey=new StringBuffer().append(killId).append(userId).append("-RedissonLock").toString();
RLock lock=redissonClient.getLock(lockKey);
try {
Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS);
if (cacheRes){
//TODO:核心業務邏輯的處理
if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
int res=itemKillMapper.updateKillItemV2(killId);
if (res>0){
commonRecordKillSuccessInfo(itemKill,userId);
result=true;
}
}
}else{
throw new Exception("redisson-您已經搶購過該商品了!");
}
}
}finally {
lock.unlock();
//lock.forceUnlock();
}
return result;
}
代碼優化3:
@Autowired
private CuratorFramework curatorFramework;
private static final String pathPrefix="/kill/zkLock/";
/**
* 商品秒殺核心業務邏輯的處理-基于ZooKeeper的分布式鎖
* @param killId
* @param userId
* @return
* @throws Exception
*/
@Override
public Boolean killItemV5(Integer killId, Integer userId) throws Exception {
Boolean result=false;
InterProcessMutex mutex=new InterProcessMutex(curatorFramework,pathPrefix+killId+userId+"-lock");
try {
if (mutex.acquire(10L,TimeUnit.SECONDS)){
//TODO:核心業務邏輯
if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
int res=itemKillMapper.updateKillItemV2(killId);
if (res>0){
commonRecordKillSuccessInfo(itemKill,userId);
result=true;
}
}
}else{
throw new Exception("zookeeper-您已經搶購過該商品了!");
}
}
}catch (Exception e){
throw new Exception("還沒到搶購日期、已過了搶購時間或已被搶購完畢!");
}finally {
if (mutex!=null){
mutex.release();
}
}
return result;
}