中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站ip訪問做圖表中國十大新聞網(wǎng)站排名

網(wǎng)站ip訪問做圖表,中國十大新聞網(wǎng)站排名,傳媒公司網(wǎng)站php源碼,外國人做免費(fèi)視頻網(wǎng)站大家好,我是墨哥(隱墨星辰)。今天的內(nèi)容來源于兩個(gè)線上問題,主要和大家聊聊為什么支付系統(tǒng)中基本只使用事務(wù)模板方法,而不使用聲明式事務(wù)Transaction注解,以及使用afterCommit()出現(xiàn)連接未按預(yù)期釋放導(dǎo)致的…

大家好,我是墨哥(隱墨星辰)。今天的內(nèi)容來源于兩個(gè)線上問題,主要和大家聊聊為什么支付系統(tǒng)中基本只使用事務(wù)模板方法,而不使用聲明式事務(wù)@Transaction注解,以及使用afterCommit()出現(xiàn)連接未按預(yù)期釋放導(dǎo)致的性能問題。

1. 為什么不使用@Transaction注解

以前寫管理平臺的代碼時(shí),經(jīng)常使用@Transaction注解,也就是所謂的聲明式事務(wù),簡單而實(shí)用,但是在做支付后,基本上沒有使用@Transaction,全部使用事務(wù)模板來做。主要有兩個(gè)考慮:

1)事務(wù)的粒度控制不夠靈活,容易出現(xiàn)長事務(wù)

@Transactional注解通常應(yīng)用于方法級別,這意味著被注解的方法將作為一個(gè)整體運(yùn)行在事務(wù)上下文中。在復(fù)雜的支付流程中,需要做各種運(yùn)算處理,很多前置處理是不需要放在事務(wù)里面的。

而使用事務(wù)模板的話,就可以更精細(xì)的控制事務(wù)的開始和結(jié)束,以及更細(xì)粒度的錯(cuò)誤處理邏輯。

@Transactional
public PayOrder process(PayRequest request) {validate(request);PayOrder payOrder = buildOrder(request);save(payOrder);// 其它處理otherProcess(payOrder);
}

比如上面的校驗(yàn),構(gòu)建訂單,其它處理都不需要放在事務(wù)中。

如果把@Transactional從process()中拿走,放到save()方法,也會面臨另外的問題:otherProcess()依賴數(shù)據(jù)庫保存成功后才能執(zhí)行,如果保存失敗,不能執(zhí)行otherProcess()處理。全部考慮進(jìn)來后,使用注解處理起來就很麻煩。

2)事務(wù)傳播行為的復(fù)雜性

@Transactional注解支持不同的事務(wù)傳播行為,雖然這提供了靈活性,但在實(shí)際應(yīng)用中,錯(cuò)誤的事務(wù)傳播配置可能導(dǎo)致難以追蹤的問題,如意外的事務(wù)提交或回滾。

而且經(jīng)常有多層子函數(shù)調(diào)用,很容易子函數(shù)有一個(gè)耗時(shí)操作(比如RPC調(diào)用或請求外部應(yīng)用),一方面可能出事長事務(wù),另一方面還可能因?yàn)橥庹{(diào)拋異步,導(dǎo)致事務(wù)回滾,數(shù)據(jù)庫中都沒有記錄保存。

以前就在生產(chǎn)上碰到過類似的問題,因?yàn)樵诟阜椒ㄊ褂昧?#64;Transactional注解,子函數(shù)拋出異常,去數(shù)據(jù)庫找問題單據(jù),竟然沒有記錄,翻代碼一行行看,才發(fā)現(xiàn)問題。

2. afterCommit存在的問題及解法

有一次參與線上壓測,在流量上去后,應(yīng)用持續(xù)報(bào)獲取數(shù)據(jù)庫連接超時(shí),排查很久才找到原因,問題非常經(jīng)典,值得和大家聊聊。

無論在支付系統(tǒng),還是電商系統(tǒng),還是其它各種業(yè)務(wù)系統(tǒng),都存在這樣的需求:在一個(gè)事務(wù)中既保存多個(gè)數(shù)據(jù)庫表,又要外發(fā)請求,且這個(gè)外發(fā)請求耗時(shí)很長。

比如:方法A保存數(shù)據(jù)庫表A,方法B保存數(shù)據(jù)庫表B,并且要外發(fā)給其它系統(tǒng)且耗時(shí)長,方法C要保存數(shù)據(jù)庫表C。這三個(gè)方法需要在一個(gè)事務(wù)里面。

我見過三種方案:

方案一:不管三七二十一,就直接放在一個(gè)事務(wù)中。請求量不大時(shí),看不出長事務(wù)的影響。

方案二:知道使用Spring提供的模板方法:TransactionSynchronizationAdapter.afterCommit()。外發(fā)請求耗時(shí)長過長時(shí),在大并發(fā)下仍然有連接未能及時(shí)釋放的問題。

方案三:自己實(shí)現(xiàn)事務(wù)模板方法,在Spring提交事務(wù)并釋放連接后,再執(zhí)行耗時(shí)長的外發(fā)。

第一種沒什么好說的,下面介紹方案二和方案三,兩者區(qū)別如下圖所示:

在支付系統(tǒng)中,經(jīng)常需要做一些流程編排,這些流程操作需要放在一個(gè)事務(wù)中,比如先保存主單據(jù),再保存流水單據(jù),然后外發(fā)銀行請求扣款,有同學(xué)寫的代碼類似這樣:

主方法偽代碼(流程引擎入口):

public void process(FlowContext context) {// 獲取流程處理鏈List<FlowProcess> flows = fetchFlow(context);for (FlowProcess flow : flows) {// 使用事務(wù)模板dataSourceManager.getTransactionTemplate().execute(status -> {// 執(zhí)行子流程flow.execute(context); // 更新主單信息context.getPayOrder().putJournal(context.getJournal());context.getPayOrder().transToNextStatus(context.getJournal().getTargetStatus());save(context.getPayOrder());return true;});}
}

其中一個(gè)外發(fā)銀行子流程偽代碼

public void execute(FlowContext context) {Journal journal = buildJournal(context);// 子函數(shù)里面保存了3張表的數(shù)據(jù)save(journal);TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {// 事務(wù)提交后,再發(fā)送給外部銀行g(shù)atewayService.sendToChannel(journal);}});
}

預(yù)期是事務(wù)提交后再調(diào)用發(fā)給銀行。

但是實(shí)際情況卻是,Spring提交事務(wù)后,調(diào)用了afterCommit(),但是并沒有釋放連接,導(dǎo)致在外發(fā)銀行的長達(dá)1000多毫秒的時(shí)間內(nèi),數(shù)據(jù)庫連接一直在保持,而不是提交事務(wù)后馬上歸還了連接,加上線上服務(wù)器的連接數(shù)只分配了30個(gè)給每臺應(yīng)用。這就意味著最大并發(fā)也小于30。

通過查看AbstractPlatformTransactionManager.java,發(fā)現(xiàn)是先調(diào)用:riggerAfterCommit(status),然后才清理并釋放連接:cleanupAfterCompletion(status)。

    private void processCommit(DefaultTransactionStatus status) throws TransactionException {try {// 其它代碼省略... ...// Trigger afterCommit callbacks, with an exception thrown there// propagated to callers but the transaction still considered as committed.try {triggerAfterCommit(status);}// 其它代碼省略... ...}finally {cleanupAfterCompletion(status);}}

解決辦法:自己創(chuàng)建一個(gè)事務(wù)模板,實(shí)現(xiàn)afterCommit()。

public class FlowTransactionTemplate { public static <R> R execute(FlowContext context, Supplier<R> callback) { TransactionTemplate template = context.getTransactionTemplate();Assert.notNull(template, "transactionTemplate cannot be null"); PlatformTransactionManager transactionManager = template.getTransactionManager();Assert.notNull(transactionManager, "transactionManager cannot be null"); boolean commit = false;try {TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); // Corrected "TranscationStatus" to "TransactionStatus"R result = null;try {result = callback.get();} catch (Exception e) {transactionManager.rollback(status); throw e;}transactionManager.commit(status);commit = true;return result;} finally {if (commit) {invokeAfterCommit(context);}}}private static void invokeAfterCommit(FlowContext context) {try {context.invokeAfterCommit();} catch (Exception e) {// 打印日志... ...}}
}

FlowContext加上事務(wù)提交后的執(zhí)行的鉤子方法,在鉤子方法中實(shí)現(xiàn)一些長耗時(shí)工作:

public class FlowContext {// 其它代碼不變... ...private List<AfterCommitHook> afterCommitHooks = new ArrayList<>();public void registerAfterCommitHook(AfterCommitHook hook) {afterCommitHooks.add(hook);}public void invokeAfterCommit() {try {for(AfterCommitHook hook : afterCommitHooks) {hook.afterCommit();}} catch (Exception e) {// 異常處理... ...} finally {// 鉤子已執(zhí)行完,清理掉afterCommitHooks.clear();}}public static abstract class AfterCommitHook {public abstract void afterCommit();}
}

主流程修改為直接調(diào)用:FlowTranscationTemplate.execute。

public void process(FlowContext context) {context.setTransactionTemplate(dataSourceManager.getTransactionTemplate());List<FlowProcess> flows = fetchFlow(context);for (FlowProcess flow : flows) {// 把Spring模板方法修改自己的模板方法,其它不變FlowTransactionTemplate.execute(context, () -> {flow.execute(context);context.getPayOrder().putJournal(context.getJournal());context.getPayOrder().transToNextStatus(context.getJournal().getTargetStatus());save(context.getPayOrder());return true;});}
}

子流程修改為把a(bǔ)fterCommit要做的事注冊到流程上下文中:

public void execute(FlowContext context) {Journal journal = buildJournal(context);// 子函數(shù)里面保存了3張表的數(shù)據(jù)save(journal);// 把外發(fā)動(dòng)作注冊到流程上下文中的鉤子方法中,// 而不是直接使用Spring原生的TransactionSynchronizationAdapter.afterCommit()// 其它保持不變context.registerAfterCommitHook(() -> {// 事務(wù)提交后發(fā)給銀行g(shù)atewayService.sendToChannel(journal);});
}

這樣處理的優(yōu)點(diǎn)有幾個(gè):

  1. 清晰的事務(wù)邊界管理:通過顯式控制事務(wù)的提交和回調(diào)執(zhí)行,增加了代碼的可控性。
  2. 資源使用優(yōu)化:確保數(shù)據(jù)庫連接在不需要時(shí)能夠及時(shí)釋放,提升了資源的使用效率。
  3. 靈活的后續(xù)操作擴(kuò)展:允許注冊多個(gè)回調(diào),方便地添加事務(wù)提交后需要執(zhí)行的操作,增強(qiáng)了代碼的擴(kuò)展性和復(fù)用性。

有個(gè)注意的點(diǎn),就是確保invokeAfterCommit的穩(wěn)健性,代碼里是通過捕獲異常打印日志,避免對其它操作有影響。

3. 擴(kuò)展:長事務(wù)

長事務(wù)指的是在數(shù)據(jù)庫管理和應(yīng)用開發(fā)中,持續(xù)時(shí)間較長的事務(wù)處理過程。一般來說,在分布式應(yīng)用中,每個(gè)服務(wù)器分配的連接數(shù)是有限的,比如每個(gè)服務(wù)器20個(gè)連接,這就要求我們必要盡量減少長事務(wù),以便處理更多請求。

典型的方案有:

1)非事務(wù)類操作,就放在事務(wù)外面。比如前置處理,先請求下游獲取資源,做各種校驗(yàn),全部通過后,再啟動(dòng)事務(wù)。還有就是使用hook的方式,等事務(wù)提交后,再請求外部耗時(shí)的服務(wù)。

2)事務(wù)拆分。把一個(gè)長事務(wù)拆分為多個(gè)短事務(wù)。

3)異步處理。有點(diǎn)類似hook的方案。

4. 結(jié)束語

Spring事務(wù)管理提供了強(qiáng)大而靈活的機(jī)制來處理復(fù)雜的業(yè)務(wù)邏輯,但是每個(gè)特性和工具的使用都需要對其行為有深入的理解,而不能想當(dāng)然。比如文中的afterCommit就是這樣一個(gè)典型例子。

自定義事務(wù)模板的實(shí)踐向我們展示了,雖然@Transcation注解很方便,但在一些特殊場景下,需要我們深入了解框架的工作原理并結(jié)合實(shí)際業(yè)務(wù)需求,既高效地利用Spring提供的工具,同時(shí)也規(guī)避潛在的坑點(diǎn)。

希望本文能夠幫助讀者更好地理解和應(yīng)用Spring事務(wù)管理中的afterCommit鉤子,以及如何在對資源或性能要求很嚴(yán)格的情況下,比如支付場景,如何定義自己的事務(wù)模板,幫助我們構(gòu)建更健壯、更高效的應(yīng)用。

這是《百圖解碼支付系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)》專欄系列文章中的第(30)篇。和墨哥(隱墨星辰)一起深入解碼支付系統(tǒng)的方方面面。

歡迎轉(zhuǎn)載。

Github(PDF文檔全集,不定時(shí)更新):GitHub - yinmo-sc/Decoding-Payment-System-Book: 百圖解碼支付系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

精選

專欄地址百圖解碼支付系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)
《百圖解碼支付系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)》專欄介紹
《百圖解碼支付系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)》專欄大綱及文章鏈接匯總(進(jìn)度更新于2023.2.4)
領(lǐng)域相關(guān)(部分)
支付行業(yè)黑話:支付系統(tǒng)必知術(shù)語一網(wǎng)打盡
跟著圖走,學(xué)支付:在線支付系統(tǒng)設(shè)計(jì)的圖解教程
圖解收單平臺:打造商戶收款的高效之道
圖解結(jié)算平臺:準(zhǔn)確高效給商戶結(jié)款
圖解收銀臺:支付系統(tǒng)承上啟下的關(guān)鍵應(yīng)用
圖解支付引擎:資產(chǎn)流動(dòng)的樞紐
圖解渠道網(wǎng)關(guān):不只是對接渠道的接口(一)

技術(shù)專題(部分)
交易流水號的藝術(shù):掌握支付系統(tǒng)的業(yè)務(wù)ID生成指南
揭密支付安全:為什么你的交易無法被篡改
金融密語:揭秘支付系統(tǒng)的加解密藝術(shù)
支付系統(tǒng)日志設(shè)計(jì)完全指南:構(gòu)建高效監(jiān)控和問題排查體系的關(guān)鍵基石
避免重復(fù)扣款:分布式支付系統(tǒng)的冪等性原理與實(shí)踐
支付系統(tǒng)的心臟:簡潔而精妙的狀態(tài)機(jī)設(shè)計(jì)與核心代碼實(shí)現(xiàn)
精確掌控并發(fā):固定時(shí)間窗口算法在分布式環(huán)境下并發(fā)流量控制的設(shè)計(jì)與實(shí)現(xiàn)
精確掌控并發(fā):滑動(dòng)時(shí)間窗口算法在分布式環(huán)境下并發(fā)流量控制的設(shè)計(jì)與實(shí)現(xiàn)

http://www.risenshineclean.com/news/60258.html

相關(guān)文章:

  • 石家莊網(wǎng)絡(luò)公司行業(yè)深圳百度seo怎么做
  • 微網(wǎng)站做的比較好搜索引擎營銷的主要方式有
  • 南京網(wǎng)站網(wǎng)站建設(shè)學(xué)校如何發(fā)布一個(gè)網(wǎng)站
  • 做金融必看網(wǎng)站谷歌在線瀏覽器免費(fèi)入口
  • 網(wǎng)站建設(shè)欄目說明百度一下就知道官網(wǎng)
  • 企業(yè)網(wǎng)站建設(shè)的思路最優(yōu)化方法
  • 一些做的好的網(wǎng)站東營百度推廣電話
  • 曲阜公司網(wǎng)站建設(shè)價(jià)格便宜ui設(shè)計(jì)培訓(xùn)班哪家好
  • 淘寶客網(wǎng)站還可以做嗎牛奶軟文廣告營銷
  • 長沙今天最新招聘信息臺州關(guān)鍵詞優(yōu)化平臺
  • 阿里巴巴做網(wǎng)站的電話號碼西安百度推廣怎么做
  • 金融投資網(wǎng)站開發(fā)站長工具是干嘛的
  • 產(chǎn)品經(jīng)理培訓(xùn)哪個(gè)機(jī)構(gòu)好湖南正規(guī)seo優(yōu)化
  • 做網(wǎng)站的人還能做什么公司網(wǎng)站建設(shè)要多少錢
  • 做網(wǎng)站怎樣連數(shù)據(jù)庫關(guān)鍵詞推廣效果分析
  • 做視頻網(wǎng)站視頻放在哪里域名怎么注冊
  • 如何做電影網(wǎng)站賺錢嗎刷百度指數(shù)
  • 營銷型網(wǎng)站建設(shè)公司云服務(wù)器
  • 網(wǎng)站建設(shè)捌金手指下拉一南寧推廣軟件
  • 瓊海網(wǎng)站建設(shè)太原seo報(bào)價(jià)
  • 目前最火的自媒體平臺seo網(wǎng)站關(guān)鍵詞排名優(yōu)化公司
  • 做韓國網(wǎng)站有哪些東西嗎手機(jī)網(wǎng)站排名優(yōu)化軟件
  • 響應(yīng)式網(wǎng)站底部菜單欄廣州競價(jià)托管公司
  • 廣州網(wǎng)站建站房管局備案查詢網(wǎng)站
  • 用.net core 做網(wǎng)站公司企業(yè)網(wǎng)站建設(shè)
  • 新手學(xué)做網(wǎng)站的教學(xué)書百度怎么搜索圖片
  • 做導(dǎo)航網(wǎng)站賺錢嗎搜索推廣公司
  • 滿屏網(wǎng)站做多大尺寸網(wǎng)絡(luò)平臺建設(shè)及運(yùn)營方案
  • 山西做網(wǎng)站的企業(yè)站長工具官網(wǎng)域名查詢
  • 公司網(wǎng)站做地圖地址互聯(lián)網(wǎng)推廣怎么找渠道