如何通過(guò)SpringBoot實(shí)現(xiàn)商城秒殺系統(tǒng)
這篇文章主要介紹了如何通過(guò)SpringBoot實(shí)現(xiàn)商城秒殺系統(tǒng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
學(xué)習(xí)自:地址
1.主要流程
1.1數(shù)據(jù)庫(kù):
1.2 環(huán)境
window下:Zookeeper,Redis,rabbitmq-server。jdk1.8以上。
1.3 介紹
這里只做秒殺部分功能,其他功能不會(huì)涉及。項(xiàng)目運(yùn)行后可訪問(wèn)秒殺商品頁(yè)面
當(dāng)用戶沒(méi)登陸,點(diǎn)擊詳情會(huì)跳轉(zhuǎn)到登陸頁(yè)面。
用戶登陸后可以查看商品的詳情并進(jìn)行搶購(gòu)。
注意,用戶對(duì)于一件商品只能搶購(gòu)一次,進(jìn)行第二次搶購(gòu)時(shí)會(huì)被拒絕。當(dāng)用戶搶購(gòu)成功時(shí)會(huì)異步發(fā)送一封郵件給用戶。
主要邏輯就是以上。接下來(lái)看代碼
1.4 項(xiàng)目結(jié)構(gòu),api封裝一些枚舉和返回值,model主要是實(shí)體類和sql映射文件,service實(shí)現(xiàn)業(yè)務(wù)邏輯代碼。
1.5 顯示秒殺商品到頁(yè)面以及用戶的操作使用的還是MVC模式,不細(xì)講。主要看如實(shí)現(xiàn)高并發(fā)下的秒殺。
要細(xì)述的話,東西太多,如果想深入了解,可點(diǎn)擊上面的鏈接。
基本的秒殺邏輯如下,判斷用戶是否已經(jīng)搶購(gòu)過(guò)該商品,如果沒(méi)有則查詢待秒殺商品詳情,判斷該商品是否可以別秒殺,判斷依據(jù)為庫(kù)存是否足夠
如果符合條件,則該商品庫(kù)存減1,接著,再一次判斷扣減是否成功,如果扣減成功則生成秒殺成功的訂單,同時(shí)通知用戶秒殺成功的信息。
public Boolean killItem(Integer killId, Integer userId) throws Exception { Boolean result=false; //TODO:判斷當(dāng)前用戶是否已經(jīng)搶購(gòu)過(guò)當(dāng)前商品 if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){ //TODO:查詢待秒殺商品詳情 ItemKill itemKill=itemKillMapper.selectById(killId); //TODO:判斷是否可以被秒殺canKill=1? if (itemKill!=null && 1==itemKill.getCanKill() ){ //TODO:扣減庫(kù)存-減一 int res=itemKillMapper.updateKillItem(killId); //TODO:扣減是否成功?是-生成秒殺成功的訂單,同時(shí)通知用戶秒殺成功的消息 if (res>0){ commonRecordKillSuccessInfo(itemKill,userId); result=true; } } }else{ throw new Exception("您已經(jīng)搶購(gòu)過(guò)該商品了!"); } return result; }
代碼優(yōu)化1:使用redis的分布式鎖,使用當(dāng)前秒殺商品的id和當(dāng)前用戶的id組成一個(gè)key,使用StringBuffer拼接,使用雪花算法生成一個(gè)value,存進(jìn)redis中。
@Autowired private StringRedisTemplate stringRedisTemplate; /** * 商品秒殺核心業(yè)務(wù)邏輯的處理-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的原子操作實(shí)現(xiàn)分布式鎖-對(duì)共享操作-資源進(jìn)行控制 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腳本提供“分布式鎖服務(wù)”,就可以寫(xiě)在一起 //TOOD:redis部署節(jié)點(diǎn)宕機(jī)了 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("還沒(méi)到搶購(gòu)日期、已過(guò)了搶購(gòu)時(shí)間或已被搶購(gòu)?fù)戤叄?); }finally { if (value.equals(valueOperations.get(key).toString())){ stringRedisTemplate.delete(key); } } } }else{ throw new Exception("Redis-您已經(jīng)搶購(gòu)過(guò)該商品了!"); } return result; }
代碼優(yōu)化2:將 Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS); 每隔30秒判斷當(dāng)前用戶是否超時(shí)寫(xiě)在了鎖外面,不會(huì)因?yàn)橐淮慰D而影響整個(gè)程序。
@Autowired private RedissonClient redissonClient; /** * 商品秒殺核心業(yè)務(wù)邏輯的處理-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:核心業(yè)務(wù)邏輯的處理 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-您已經(jīng)搶購(gòu)過(guò)該商品了!"); } } }finally { lock.unlock(); //lock.forceUnlock(); } return result; }
代碼優(yōu)化3:
@Autowired private CuratorFramework curatorFramework; private static final String pathPrefix="/kill/zkLock/"; /** * 商品秒殺核心業(yè)務(wù)邏輯的處理-基于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:核心業(yè)務(wù)邏輯 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-您已經(jīng)搶購(gòu)過(guò)該商品了!"); } } }catch (Exception e){ throw new Exception("還沒(méi)到搶購(gòu)日期、已過(guò)了搶購(gòu)時(shí)間或已被搶購(gòu)?fù)戤叄?); }finally { if (mutex!=null){ mutex.release(); } } return result; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java 中的FileReader和FileWriter源碼分析_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
本文給大家分享一段示例程序,通過(guò)示例代碼可以看出FileReader是基于InputStreamReader實(shí)現(xiàn)的,FileWriter是基于OutputStreamWriter實(shí)現(xiàn)的,具體程序代碼大家通過(guò)本文了解下吧2017-05-05SpringBoot多模塊搭建的實(shí)現(xiàn)示例
多模塊開(kāi)發(fā)是指將一個(gè)大型應(yīng)用程序拆分為多個(gè)模塊,本文主要介紹了SpringBoot多模塊搭建的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08Java三種獲取redis的連接及redis_String類型演示(適合新手)
這篇文章主要介紹了Java三種獲取redis的連接及redis_String類型演示(適合新手),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12詳解使用Spring快速創(chuàng)建web應(yīng)用的兩種方式
這篇文章主要介紹了詳解使用Spring快速創(chuàng)建web應(yīng)用的兩種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11Springboot+netty實(shí)現(xiàn)Web聊天室
這篇文章主要介紹了利用springboot+netty實(shí)現(xiàn)一個(gè)簡(jiǎn)單Web聊天室,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)Java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-12-12使用迭代器Iterator遍歷Collection問(wèn)題
這篇文章主要介紹了使用迭代器Iterator遍歷Collection問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11SpringBoot響應(yīng)Json數(shù)據(jù)亂碼通過(guò)配置的解決
這篇文章主要介紹了SpringBoot響應(yīng)Json數(shù)據(jù)亂碼通過(guò)配置的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11springboot?vue接口測(cè)試前后端樹(shù)節(jié)點(diǎn)編輯刪除功能
這篇文章主要為大家介紹了springboot?vue接口測(cè)試前后端樹(shù)節(jié)點(diǎn)編輯刪除功能,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05