一俏蛮、問題產(chǎn)生背景
應(yīng)用上線的時(shí)候撑蚌,正常調(diào)用Tomcat的shutdown.sh腳本,事務(wù)執(zhí)行一半異常提交搏屑。偽代碼如下:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void insert(PaymentOrder paymentOrder) {
try{
paymentOrderDao.update(paymentOrder);
PaymentOrderDao.insert(paymentOrder)
}catch(Exception e){
logger.error(" 操作支付訂單失敗 biz " + paymentOrder.getBiz() + " bizOrder " + paymentOrder.getBizOrder(), e);
Throw e;
}
}
上面是一段偽代碼锨并,實(shí)際在tomcat重啟的時(shí)候,上面update語句提交睬棚,而insert沒有第煮。
二、思路解析
1抑党、直接將Tomcat服務(wù)kill掉能否重現(xiàn)問題
按之前的理解是包警,Tomcat重啟事務(wù)中斷,數(shù)據(jù)庫在事務(wù)連接超時(shí)后會回滾事務(wù)底靠。那么寫一段代碼試一下害晦,使用Kill -9命令中斷tomcat服務(wù)后發(fā)現(xiàn)數(shù)據(jù)庫事務(wù)竟然回滾了。
2暑中、分析Tomcat的shutdown.sh命令
其實(shí)shutdown.sh命令最終調(diào)用的是catalina.sh命令腳本壹瘟,看sh源碼如下:
exec "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
-Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
-Djava.io.tmpdir="$CATALINA_TMPDIR" \
org.apache.catalina.startup.Bootstrap "$@" stop
其實(shí)最終是調(diào)用的Bootstrap這個(gè)類來關(guān)閉服務(wù)的,我們再來看這個(gè)類的內(nèi)容鳄逾。
if (server instanceof Lifecycle) {
try {
((Lifecycle) server).stop();
} catch (LifecycleException e) {
log.error("Catalina.stop", e);
}
}
Tomcat是將注冊進(jìn)來的服務(wù)循環(huán)逐個(gè)關(guān)閉稻轨,這時(shí)候在關(guān)閉的時(shí)候可能會因?yàn)榍耙粋€(gè)資源關(guān)閉而造成后一個(gè)資源拋出異常,注意這個(gè)異常有可能是Throwable雕凹,也可能是Exception殴俱,后面詳細(xì)解釋。
3枚抵、分析Spring注解事務(wù)源碼
在Tomcat關(guān)閉的時(shí)候线欲,拋出的異常和上面代碼的異常沒有匹配成功,spring異常匹配采用迭代當(dāng)前異常的所有父類與目標(biāo)異常匹配汽摹,匹配不到后檢查當(dāng)前異常是否為Error或者RuntimeException的實(shí)例,是的話也能匹配上李丰,但是沒有匹配是否為Throwable的實(shí)例
三、問題總結(jié)
通過上面的問題分析逼泣,可以把代碼寫成如下樣式:
@Override
@Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
public void insert(PaymentOrder paymentOrder) {
try{
paymentOrderDao.update(paymentOrder);
PaymentOrderDao.insert(paymentOrder)
}catch(Throwable e){
logger.error(" 操作支付訂單失敗 biz " + paymentOrder.getBiz() + " bizOrder " + paymentOrder.getBizOrder(), e);
Throw e;
}
}
采用Throwable捕獲方能確保Tomcat異常重啟趴泌,事務(wù)能夠正確回滾。