學(xué)習(xí)spring事務(wù)與消息隊(duì)列
在開(kāi)發(fā)過(guò)程中,遇到一個(gè)bug,產(chǎn)生bug的原因是spring事務(wù)提交晚于消息隊(duì)列的生產(chǎn)消息,導(dǎo)致消息隊(duì)列消費(fèi)消息時(shí)獲取到的數(shù)據(jù)不正確。這篇文章介紹問(wèn)題的產(chǎn)生和一步步的解決過(guò)程。
一.問(wèn)題的產(chǎn)生:
場(chǎng)景還原:接口中的一個(gè)方法,首先修改訂單狀態(tài),然后向消息隊(duì)列中生產(chǎn)消息,消息隊(duì)列的消費(fèi)者獲取到消息檢測(cè)訂單狀態(tài),發(fā)現(xiàn)訂單狀態(tài)未更改。
代碼:
@Service(orderApi)
public class OrderApiImpl implements OrderApi {
@Resource MqService mqService;
@OrderDao orderDao;
public void push(String orderId) {
// 更新訂單狀態(tài),之前的狀態(tài)是1
updateStatus(orderId, 3);
// 產(chǎn)生消息
mqService.produce(orderId);
}
public viod updateStatus(String orderId, Integer status) {
orderDao.updateStatus(orderId, status);
}
}
問(wèn)題產(chǎn)生原因:orderApi中的所有方法都有事務(wù),事務(wù)類(lèi)型PROPAGATION_REQUIRED,所以push方法對(duì)數(shù)據(jù)的操作會(huì)在push代碼全部執(zhí)行之后提交,而在事務(wù)提交之前消息隊(duì)列的消息已經(jīng)產(chǎn)生所以消息隊(duì)列中消費(fèi)到的訂單從數(shù)據(jù)庫(kù)查詢(xún)出的狀態(tài)可能還為1。為了讓bug現(xiàn)象更明顯,可以在push方法最后添加:
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
這樣就會(huì)發(fā)現(xiàn)消費(fèi)消息時(shí),訂單狀態(tài)一定是未修改的。
二.問(wèn)題的解決:
解決方案:在更新數(shù)據(jù)時(shí),新建一個(gè)事物,保證更新代碼執(zhí)行完成后,更新數(shù)據(jù)庫(kù)的事務(wù)已被提交。(確保消息產(chǎn)生前數(shù)據(jù)庫(kù)操作已提交)
按照上述方案,我首先想到的是直接修改updateStatus方法的事務(wù)類(lèi)型;我將此方法的事務(wù)類(lèi)型改為PROPAGATION_REQUIRES_NEW(新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起)。
但是這么做有兩點(diǎn)不合適:
1.強(qiáng)制修改了updateStaus的事務(wù)類(lèi)型,可能影響其他流程。
2.未起到作用,updateStaus方法中沒(méi)有新建事務(wù)。
關(guān)于第二點(diǎn)的解釋?zhuān)簊pring添加事務(wù)是通過(guò)BeanNameAutoProxyCreator實(shí)現(xiàn)的動(dòng)態(tài)代理,只是給bean對(duì)象添加了事務(wù),現(xiàn)在在類(lèi)內(nèi)部調(diào)用方法,是不會(huì)觸發(fā)新事物的創(chuàng)建的。
所以在經(jīng)過(guò)以上嘗試后,我創(chuàng)建了一個(gè)新的類(lèi):
@Service("orderExtApi")
public class OrderExtApiImpl {
@Resource OrderApi orderApi;
public void updateStatusNewPropagation(String orderId) {
orderApi.updateStatus(orderId);
}
}
并為updateStatusNewPropagation方法添加事務(wù)PROPAGATION_REQUIRES_NEW
這個(gè)類(lèi)就只是為了給orderApi中的updateStaus方法新起一個(gè)事務(wù)。
ok,到此為止bug已經(jīng)解決了。
但是代碼中還是存在問(wèn)題:對(duì)數(shù)據(jù)庫(kù)的操作已經(jīng)提交,如果生產(chǎn)消息出現(xiàn)異常對(duì)業(yè)務(wù)邏輯來(lái)說(shuō)還是錯(cuò)誤的。所以需要檢測(cè)消息的產(chǎn)生是否完成。
最終orderApi中的代碼如下:
@Service(orderApi)
public class OrderApiImpl implements OrderApi {
@Resource MqService mqService;
@Resource OrderDao orderDao;
@Resource OrderExtApiImpl orderExtApi;
public void push(String orderId) {
// 更新訂單狀態(tài),之前的狀態(tài)是1
orderExtApi.updateStatusNewPropagation(orderId, 3);
// 產(chǎn)生消息--produce會(huì)檢測(cè)是否出現(xiàn)異常 當(dāng)返回1時(shí)表示生產(chǎn)消息成功
Response response = mqService.produce(orderId);
if (response.getCode() != 1) {
log.info("消息隊(duì)列生產(chǎn)消息異常:" + response.getErrorMsg())
// 生產(chǎn)消息異常,重置狀態(tài) 等待下次重新執(zhí)行
orderExtApi.updateStatusNewPropagation(orderId, 1);
}
}
public viod updateStatus(String orderId, Integer status) {
orderDao.updateStatus(orderId, status);
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java lombok中@Accessors注解三個(gè)屬性的作用
這篇文章主要介紹了Java?lombok的@Accessors注解屬性解析,該注解主要作用是:當(dāng)屬性字段在生成?getter?和?setter?方法時(shí),做一些相關(guān)的設(shè)置,需要的朋友可以參考下2023-05-05
SpringBoot使用Spring-Data-Jpa實(shí)現(xiàn)CRUD操作
這篇文章主要為大家詳細(xì)介紹了SpringBoot使用Spring-Data-Jpa實(shí)現(xiàn)CRUD操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
手寫(xiě)redis@Cacheable注解?參數(shù)java對(duì)象作為key值詳解
這篇文章主要介紹了手寫(xiě)redis@Cacheable注解?參數(shù)java對(duì)象作為key值詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Java編程刪除鏈表中重復(fù)的節(jié)點(diǎn)問(wèn)題解決思路及源碼分享
這篇文章主要介紹了Java編程刪除鏈表中重復(fù)的節(jié)點(diǎn)問(wèn)題解決思路及源碼分享,具有一定參考價(jià)值,這里分享給大家,供需要的朋友了解。2017-10-10
SpringBoot自動(dòng)裝配原理詳細(xì)解析
這篇文章主要介紹了SpringBoot自動(dòng)裝配原理詳細(xì)解析,一個(gè)對(duì)象交給Spring來(lái)管理的三種方式 @Bean @Compoment @Import,2024-01-01
@Bean主要在@Configuration中,通過(guò)方法進(jìn)行注入相關(guān)的Bean,@Compoent與@Service歸為一類(lèi),在類(lèi)上加注入對(duì)應(yīng)的類(lèi),需要的朋友可以參考下
Java?nacos動(dòng)態(tài)配置實(shí)現(xiàn)流程詳解
使用動(dòng)態(tài)配置的原因是properties和yaml是寫(xiě)到項(xiàng)目中的,好多時(shí)候有些配置需要修改,每次修改就要重新啟動(dòng)項(xiàng)目,不僅增加了系統(tǒng)的不穩(wěn)定性,也大大提高了維護(hù)成本,非常麻煩,且耗費(fèi)時(shí)間2022-09-09
Spring?Boot接口支持高并發(fā)具體實(shí)現(xiàn)代碼
這篇文章主要給大家介紹了關(guān)于Spring?Boot接口支持高并發(fā)具體實(shí)現(xiàn)的相關(guān)資料,在SpringBoot項(xiàng)目中通常我們沒(méi)有處理并發(fā)問(wèn)題,但是使用項(xiàng)目本身還是支持一定的并發(fā)量,需要的朋友可以參考下2023-08-08

