SpringBoot項(xiàng)目上高并發(fā)問題的解決方案
一、單機(jī)模式下高并發(fā)問題
前提:先寫一個(gè)減扣數(shù)據(jù)庫產(chǎn)品數(shù)量的一個(gè)接口作為測試。
拿以前springboot整合布隆過濾網(wǎng)篇的一個(gè)接口直接做改造:假設(shè)編號(hào)為2的蘋果庫存還有一個(gè),現(xiàn)在有個(gè)接口去買這個(gè)蘋果并生成訂單號(hào)以便于后期支付,得到如下:
1、數(shù)據(jù)表:

2、接口


通過jmeter模擬一秒鐘有100個(gè)用戶購買這個(gè)蘋果,結(jié)果會(huì)是什么?


會(huì)發(fā)現(xiàn)直接賣爆了,一個(gè)蘋果被賣了幾十單。怎么解決這個(gè)并發(fā)問題呢?
PS:java提供了鎖來處理
1、樂觀鎖
CAS先比較再交換,Java中提供了Atomic開頭的類,例如AtomicInteger、AtomicLong、AtomicReference等原子類都是此思想來支持CAS操作的。進(jìn)行如下改造,來實(shí)現(xiàn)先比較在修改值的方式解決該問題。其實(shí)就是在把cas想做是一個(gè)原子操作。改造方式就是例如給商品表增加一個(gè)字段用來表示該次原子性操作時(shí),他應(yīng)該是什么值,若是則修改,不然就不修改。如下:

增加一個(gè)number字段,原理就是每次修改時(shí)帶上這個(gè)number條件,而每次減少count后修改number的值(原子性)第一個(gè)請(qǐng)求的用戶這樣處理,其他同時(shí)查到這個(gè)訂單的其他用戶,在減少count時(shí)根據(jù)number條件卻查不到這個(gè)訂單了從而無法再生成訂單。代碼如下:

繼續(xù)jmeter測試,再看看結(jié)果如何?

發(fā)現(xiàn)通過這種方式的確實(shí)現(xiàn)了防止超賣的現(xiàn)象。
優(yōu)點(diǎn):不用加鎖,不會(huì)阻塞其他線程,性能相比較好。缺點(diǎn):需要增加表字段,并且由于是在數(shù)據(jù)庫層面保持原子性可能導(dǎo)致多事務(wù)操作操作同一數(shù)據(jù)時(shí)導(dǎo)致沖突,引起數(shù)據(jù)一致性問題。
結(jié)論:所以在并發(fā)較少的情況下可以使用樂觀鎖方式。
2、悲觀鎖
將通過下面兩種鎖來進(jìn)行演示。
2.1、synchronized鎖
改造代碼如下:

通過測試得出:

發(fā)現(xiàn)實(shí)現(xiàn)了防止超賣,但是synchronized鎖是基于jvm層面的,因此并不適用于集群模式。集群模式會(huì)涉及到一個(gè)服務(wù)的多實(shí)例,就會(huì)有多個(gè)jvm,synchronized只能保證當(dāng)前實(shí)例在當(dāng)前jvm下的原子性操作。
我們用idea模擬一個(gè)集群來進(jìn)行測試,如下:




執(zhí)行一下jmeter,看看結(jié)果是什么?



我們可以看到模擬的每一個(gè)機(jī)器都搶到了一個(gè),那依舊完?duì)僮恿搜健?/p>
結(jié)論:集群模式下synchronized不可取。
2.2、Lock鎖
相比synchronized而言,這個(gè)鎖是方法,而synchronized是關(guān)鍵字。使用lock的實(shí)現(xiàn)ReentrantLock

改造代碼如下:

繼續(xù)在模擬集群下進(jìn)行測試,結(jié)果如下:



結(jié)果和synchronized效果一樣,只有在單機(jī)模式下可以保證沒問題,而集群模式下依然會(huì)出現(xiàn)問題。
結(jié)論:集群模式下Lock鎖不可取。
二、集群模式下高并發(fā)問題
上面講了單機(jī)模式下可以采用的方式解決并發(fā)問題,但是有些方式在集群模式下就不可用了,下面就試一下在集群模式下依舊可以解決并發(fā)問題的方法。
還是先看看不做任何處理的集群下進(jìn)行搶商品是什么情況?



簡直是炸裂,這樣上線不被領(lǐng)導(dǎo)懟著鼻子。
那我們怎么改造呢?我們引入Redisson。
我們直接使用前面整合布隆過濾網(wǎng)的demo,就不講整合Redisson了,已經(jīng)講過了,直接這里使用。
改造后的代碼如下:

jmeter執(zhí)行后的結(jié)果如下:



三臺(tái)機(jī)器只有一臺(tái)搶到了一個(gè)蘋果,達(dá)到了目的。Redisson的這個(gè)分布式鎖的使用也很簡單,如果服務(wù)掛掉,無法執(zhí)行final的代碼會(huì)如何,如下看看:

我們打個(gè)斷點(diǎn)假設(shè)服務(wù)在獲取鎖后服務(wù)掛了,redis如下:

可10秒后,如下:

鎖已經(jīng)過期失效不見了。因此并不會(huì)導(dǎo)致死鎖的發(fā)生,這個(gè)分布式鎖的具體實(shí)現(xiàn)大佬們可以評(píng)論區(qū)交流談?wù)摶蛘吆竺嬖倮^續(xù)說。
以上就是SpringBoot項(xiàng)目上高并發(fā)問題的解決方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot高并發(fā)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring boot2X Consul如何通過RestTemplate實(shí)現(xiàn)服務(wù)調(diào)用
這篇文章主要介紹了spring boot2X Consul如何通過RestTemplate實(shí)現(xiàn)服務(wù)調(diào)用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
StringUtils里的isEmpty方法和isBlank方法的區(qū)別詳解
這篇文章主要介紹了StringUtils里的isEmpty方法和isBlank方法的區(qū)別詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2020-04-04
如何解決@PutMapping或@PostMapping接收String類型參數(shù)多兩個(gè)“引號(hào)問題
這篇文章主要介紹了如何解決@PutMapping或@PostMapping接收String類型參數(shù)多兩個(gè)“引號(hào)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Java鎖的升級(jí)策略 偏向鎖 輕量級(jí)鎖 重量級(jí)鎖
在本文中小編給的大家整理了關(guān)于Java鎖的升級(jí)策略 偏向鎖 輕量級(jí)鎖 重量級(jí)鎖的相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們參考下。2019-06-06
SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽超詳細(xì)講解
這篇文章主要介紹了SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-11-11

