java设计前期工作基础和存在的困难_Java秒杀系统实战系列-基于Redisson的分布式锁优化秒杀逻辑...
本文是“Java秒殺系統實戰系列文章”的第十五篇,本文我們將借助綜合中間件Redisson優化“秒殺系統中秒殺的核心業務邏輯”,解決Redis的原子操作在優化秒殺邏輯過程中出現的部分瑕疵。
Redisson,字如其名,是搭建在緩存中間件Redis的基礎之上的一款綜合中間件,除了擁有Redis本身提供的強大功能之外,還提供了諸如分布式鎖、分布式服務、延遲隊列、遠程調用等強大的功能(從名字就可以看出來了:Redis + son,猶如Redis的兒子,兒子不僅繼承了老爸強大的血脈,而且還自己修煉、發展出了屬于自己的一套本領)。
在本篇文章中,我們將使用Redisson中間件其中一個強大的功能組件“分布式鎖”,用以解決秒殺系統中高并發產生的多線程對于共享資源/代碼塊的訪問所導致的“并發安全”問題!
而之所以需要Redisson這一組件,是因為在上一篇文章中,我們在采用Redis解決秒殺系統中出現的“庫存超賣”、“重復秒殺”等問題時所對應的代碼存在著瑕疵,即在使用Redis的SetNX操作之前、而還沒來得及執行Expire操作的時候,Redis的節點如果恰好出現宕機或者服務不能用的情況,那將會導致相應的Key永遠存在于緩存中,而處于“被鎖死”的狀態!
Redisson分布式鎖的出現可以很好地解決這種問題,其底層的實現機制在于“Redisson內部提供了一個監控鎖的看門狗,它的作用是在Redisson實例被關閉前,不斷的延長鎖的有效期”,除此之外,Redisson還通過加鎖的方法提供了leaseTime的參數來指定加鎖的時間,即超過這個時間后鎖便自動解開了。
接下來,我們將基于SpringBoot搭建的秒殺系統整合Redisson,加入其相關的依賴以及配置,并使用其“分布式鎖”組件徹底解決秒殺過程中出現的“庫存超賣”以及“重復秒殺”等問題。
(1)首先,需要加入Redisson的依賴,版本號為3.8.2,如下所示:
org.redisson redisson ${redisson.version}然后需要在配置文件application.properties中加入Redis服務所在的Host、Port等信息,如下所示:
#spring.redis.password=redis.config.host=redis://127.0.0.1:6379(2)緊接著,是基于Spring Boot自定義注入Redisson相關操作的Bean組件,其中,主要是RedissonClient 操作組件的自定義注入,其完整源代碼如下所示:
/** * redisson通用化配置 * @Author:debug (SteadyJack) * @Date: 2019/7/2 10:57 **/@Configurationpublic class RedissonConfig { @Autowired private Environment env; @Bean public RedissonClient redissonClient(){ Config config=new Config(); config.useSingleServer() .setAddress(env.getProperty("redis.config.host")) .setPassword(env.getProperty("spring.redis.password")); RedissonClient client=Redisson.create(config); return client; }}(3)前期工作已經準備完畢,接下來我們需要將其應用到秒殺系統中 秒殺的核心操作邏輯,在KillService服務類中我們開辟了一個新的處理方法,即killItemV4,其完整的源代碼如下所示:
@Autowiredprivate RedissonClient redissonClient;//商品秒殺核心業務邏輯的處理-redisson的分布式鎖@Overridepublic 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 { //TODO:第一個參數30s=表示嘗試獲取分布式鎖,并且最大的等待獲取鎖的時間為30s //TODO:第二個參數10s=表示上鎖之后,10s內操作完畢將自動釋放鎖 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 { //TODO:釋放鎖 lock.unlock(); } return result;}從該源代碼中,我們主要是使用了Redisson分布式鎖中的“可重入鎖”組件,其使用需要經過如下幾個步驟:
A.需要嘗試去獲取鎖,其對應的代碼以及注釋如下所示:
//TODO:第一個參數30s=表示嘗試獲取分布式鎖,并且最大的等待獲取鎖的時間為30s//TODO:第二個參數10s=表示上鎖之后,10s內操作完畢將自動釋放鎖Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS);B.在獲取到鎖之后,即cacheRes=true,即可進入秒殺核心業務邏輯的處理;同時在處理完成之后,需要釋放鎖,如下所示:
//TODO:釋放鎖lock.unlock();(4)至此,基于Redisson的分布式鎖解決高并發業務場景下,并發多線程對于共享資源/共享代碼塊的并發訪問所出現的并發安全的問題的代碼實戰已經完畢了!
我們接下來進入壓測環節,仍然以之前的測試用例為例,即killId=3的待秒殺商品的可秒殺數量total=6,可以隨機選取的用戶Id列表的總數為10個,其取值為10040~10049,則理論上最好的結果是:total最終變為=0,同時item_kill_success有6條用戶秒殺成功后生成的訂單記錄。
這個時候,我們嘗試將線程組中并發的線程數調整為10w,點擊啟動按鈕,稍等片刻,觀察控制臺的輸出信息以及item_kill和item_kill_success的數據庫表,查看其最終的記錄結果,如下圖所示:
對于這一結果,其實可以說是預料之中了!Redisson的分布式鎖確實可以在高并發業務場景/多線程高并發 場景下起到舉足輕重的作用。而在現實生活中,其實Debug也是建議各位小伙伴可以去研究這一綜合中間件,它完全可以替代Redis在項目中的使用,而且其提供的數據結構以及使用方式跟JavaSE中的數據結構很類似,比如List、Set、Map、Queue等等都可以在Java中找到相應的蹤影(而事實上Redisson的許多分布式組件跟數據結構正是基于Java中相應的數據結構來實現的)!
相關視頻教程可私信咨詢。
推薦閱讀:
Java商城秒殺系統的設計與實戰教程(SpringBoot版)
Java秒殺系統實戰系列-構建SpringBoot多模塊項目
Java秒殺系統實戰系列-基于Redis的原子操作優化秒殺邏輯
總結
以上是生活随笔為你收集整理的java设计前期工作基础和存在的困难_Java秒杀系统实战系列-基于Redisson的分布式锁优化秒杀逻辑...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python自带编译器如何生成exe_别
- 下一篇: 电商平台 高并发 微服务 方案_Java