亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Seata AT模式TransactionHook被刪除探究

 更新時(shí)間:2022年11月15日 10:25:06   作者:夢(mèng)想實(shí)現(xiàn)家_Z  
這篇文章主要為大家介紹了Seata AT模式TransactionHook被刪除探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

兄弟們,剛剛又給seata社區(qū)修了一個(gè)BUG,有用戶提了issue反應(yīng)TransactionHook在某些情況下不會(huì)被調(diào)用:

相關(guān)issue鏈接:github.com/seata/seata…,該用戶在issue中已經(jīng)指出了相關(guān)問(wèn)題所在:

下面我們來(lái)看一下到底是什么原因?qū)е铝松鲜?code>BUG的產(chǎn)生。

問(wèn)題定位

根據(jù)用戶的反饋,我們找到目標(biāo)源碼io.seata.tm.api.TransactionalTemplate#execute()

try {
    // 開(kāi)啟分布式事務(wù),獲取XID         
    beginTransaction(txInfo, tx);
    Object rs;
    try {
        // 執(zhí)行業(yè)務(wù)代碼
        rs = business.execute();
    } catch (Throwable ex) {
        // 3. 處理異常,準(zhǔn)備回滾.
        completeTransactionAfterThrowing(txInfo, tx, ex);
        throw ex;
    }
    // 4. 提交事務(wù).
    commitTransaction(tx, txInfo);
    return rs;
} finally {
    //5. 回收現(xiàn)場(chǎng)
    resumeGlobalLockConfig(previousConfig);
    triggerAfterCompletion();
    cleanUp();
}

問(wèn)題代碼就出在cleanUp()中,我們來(lái)看一下里面做了什么操作,最終我們定位到:

public final class TransactionHookManager {
  private static final ThreadLocal<List<TransactionHook>> LOCAL_HOOKS = new ThreadLocal<>();
  // 注冊(cè)TransactionHook
  public static void registerHook(TransactionHook transactionHook) {
      if (transactionHook == null) {
            throw new NullPointerException("transactionHook must not be null");
        }
        List<TransactionHook> transactionHooks = LOCAL_HOOKS.get();
        if (transactionHooks == null) {
            LOCAL_HOOKS.set(new ArrayList<>());
        }
        LOCAL_HOOKS.get().add(transactionHook);
    }
  // 移除當(dāng)前線程上所有TransactionHook
  public static void clear() {
      LOCAL_HOOKS.remove();
  }
}

由上面的源碼可知,cleanUp()操作時(shí)把當(dāng)前線程中的所有TransactionHook都清除掉了。也就是說(shuō),假如事務(wù)A和事務(wù)B共用同一個(gè)線程,當(dāng)事務(wù)B處理完畢后,調(diào)用了cleanUp()回收現(xiàn)場(chǎng)時(shí),把該線程當(dāng)中存儲(chǔ)的所有TransactionHook全部清除掉了,導(dǎo)致事務(wù)A的生命周期中找不到該事務(wù)對(duì)應(yīng)的TransactionHook,從而產(chǎn)生了BUG。

如何解決

通過(guò)與seata社區(qū)的大佬不斷地溝通,最終敲定以下方案:

1.改造TransactionHookManager.LOCAL_HOOKS,把數(shù)據(jù)類(lèi)型改成ThreadLocal<Map<String, List<TransactionHook>>>,Map中的key對(duì)應(yīng)分布式事務(wù)XID

2.針對(duì)當(dāng)前上下文中沒(méi)有XID,那么key就為null,因?yàn)?code>HashMap允許keynull

3.當(dāng)用戶查詢指定XID下的hook時(shí),連同keynull對(duì)應(yīng)的hook也一起返回;

  • 第一步比較好理解,因?yàn)槭聞?wù)A和事務(wù)B對(duì)應(yīng)的TransactionHook沒(méi)有被區(qū)分出來(lái),所以造成了清理事務(wù)B的TransactionHook時(shí)連同事務(wù)A的TransactionHook一起被清除,那么我們修改數(shù)據(jù)結(jié)構(gòu)來(lái)區(qū)分事務(wù)A和事務(wù)B的TransactionHook,以便清理的時(shí)候不會(huì)造成誤刪;

第二步為什么要針對(duì)沒(méi)有XID的時(shí)候也要能設(shè)置TransactionHook,因?yàn)橛羞@么一段代碼:

    private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
        try {
            // 執(zhí)行triggerBeforeBegin()
            triggerBeforeBegin();
            // 注冊(cè)分布式事務(wù),生成XID
            tx.begin(txInfo.getTimeOut(), txInfo.getName());
            // 執(zhí)行triggerAfterBegin()
            triggerAfterBegin();
        } catch (TransactionException txe) {
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                    TransactionalExecutor.Code.BeginFailure);
        }
    }

上面的代碼會(huì)產(chǎn)生一個(gè)問(wèn)題,因?yàn)槲覀兊?code>TransactionHook依賴(lài)于XID,但是triggerBeforeBegin()執(zhí)行的時(shí)候還沒(méi)有產(chǎn)生XID,所以為了能夠在沒(méi)有XID的時(shí)候也能夠讓TransactionHook生效,我們要有一個(gè)虛值key來(lái)臨時(shí)設(shè)置TransactionHook;

第三步的設(shè)計(jì)時(shí)為了在第二步的基礎(chǔ)上,當(dāng)事務(wù)開(kāi)啟后獲取XID后,要保證XID獲取前注冊(cè)的TransactionHook也要生效,我們?cè)谕ㄟ^(guò)XID查詢TransactionHook時(shí)要把虛值key對(duì)應(yīng)的TransactionHook也一起返回;

注意事項(xiàng)

在實(shí)際代碼修改中,發(fā)現(xiàn)triggerAfterCommit()、triggerAfterRollback()、triggerAfterCompletion()在被調(diào)用時(shí)始終拿不到對(duì)應(yīng)的TransactionHook,最終debug下來(lái)發(fā)現(xiàn)在調(diào)用這三個(gè)方法前,上下文中的XID被解綁了,導(dǎo)致拿到的XID為空。代碼類(lèi)似下面這樣:

try {
            // 調(diào)用triggerBeforeCommit()
            triggerBeforeCommit();
            // 提交事務(wù),清除XID
            tx.commit();
            if (Arrays.asList(GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbacked).contains(tx.getLocalStatus())) {
                throw new TransactionalExecutor.ExecutionException(tx,
                        new TimeoutException(String.format("Global transaction[%s] is timeout and will be rollback[TC].", tx.getXid())),
                        TransactionalExecutor.Code.TimeoutRollback);
            }
            // 調(diào)用triggerAfterCommit()
            triggerAfterCommit();
        } catch (TransactionException txe) {
            // 4.1 Failed to commit
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                    TransactionalExecutor.Code.CommitFailure);
        }

不過(guò)經(jīng)過(guò)我的一番查找,發(fā)現(xiàn)GlobalTransaction中是包含XID屬性的,所以果斷從GlobalTransaction對(duì)象中取XID傳進(jìn)來(lái)。

修改后的代碼如下:

try {
            // 調(diào)用triggerBeforeCommit()
            triggerBeforeCommit();
            // 提交事務(wù),清除XID
            tx.commit();
            if (Arrays.asList(GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbacked).contains(tx.getLocalStatus())) {
                throw new TransactionalExecutor.ExecutionException(tx,
                        new TimeoutException(String.format("Global transaction[%s] is timeout and will be rollback[TC].", tx.getXid())),
                        TransactionalExecutor.Code.TimeoutRollback);
            }
            // 調(diào)用triggerAfterCommit()
            triggerAfterCommit(tx.getXid());
        } catch (TransactionException txe) {
            // 4.1 Failed to commit
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                    TransactionalExecutor.Code.CommitFailure);
        }

改造后的TransactionHookManager

public final class TransactionHookManager {
    private TransactionHookManager() {
    }
    private static final ThreadLocal<Map<String, List<TransactionHook>>> LOCAL_HOOKS = new ThreadLocal<>();
    /**
     * get the current hooks
     *
     * @return TransactionHook list
     */
    public static List<TransactionHook> getHooks() {
        String xid = RootContext.getXID();
        return getHooks(xid);
    }
    /**
     * get hooks by xid
     * 
     * @param xid
     * @return TransactionHook list
     */
    public static List<TransactionHook> getHooks(String xid) {
        Map<String, List<TransactionHook>> hooksMap = LOCAL_HOOKS.get();
        if (hooksMap == null || hooksMap.isEmpty()) {
            return Collections.emptyList();
        }
        List<TransactionHook> hooks = new ArrayList<>();
        List<TransactionHook> localHooks = hooksMap.get(xid);
        if (StringUtils.isNotBlank(xid)) {
            List<TransactionHook> virtualHooks = hooksMap.get(null);
            if (virtualHooks != null && !virtualHooks.isEmpty()) {
                hooks.addAll(virtualHooks);
            }
        }
        if (localHooks != null && !localHooks.isEmpty()) {
            hooks.addAll(localHooks);
        }
        if (hooks.isEmpty()) {
            return Collections.emptyList();
        }
        return Collections.unmodifiableList(hooks);
    }
    /**
     * add new hook
     *
     * @param transactionHook transactionHook
     */
    public static void registerHook(TransactionHook transactionHook) {
        if (transactionHook == null) {
            throw new NullPointerException("transactionHook must not be null");
        }
        Map<String, List<TransactionHook>> hooksMap = LOCAL_HOOKS.get();
        if (hooksMap == null) {
            hooksMap = new HashMap<>();
            LOCAL_HOOKS.set(hooksMap);
        }
        String xid = RootContext.getXID();
        List<TransactionHook> hooks = hooksMap.get(xid);
        if (hooks == null) {
            hooks = new ArrayList<>();
            hooksMap.put(xid, hooks);
        }
        hooks.add(transactionHook);
    }
    /**
     * clear hooks by xid
     * 
     * @param xid
     */
    public static void clear(String xid) {
        Map<String, List<TransactionHook>> hooksMap = LOCAL_HOOKS.get();
        if (hooksMap == null || hooksMap.isEmpty()) {
            return;
        }
        hooksMap.remove(xid);
        if (StringUtils.isNotBlank(xid)) {
            hooksMap.remove(null);
        }
    }
}

以上就是Seata AT模式TransactionHook被刪除探究的詳細(xì)內(nèi)容,更多關(guān)于Seata AT刪除TransactionHook的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java實(shí)現(xiàn)的DES加密算法詳解

    java實(shí)現(xiàn)的DES加密算法詳解

    這篇文章主要介紹了java實(shí)現(xiàn)的DES加密算法,結(jié)合實(shí)例形式詳細(xì)分析了java實(shí)現(xiàn)DES加密操作的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-06-06
  • 將Java項(xiàng)目打包成可執(zhí)行的jar包

    將Java項(xiàng)目打包成可執(zhí)行的jar包

    這篇文章主要介紹了將Java項(xiàng)目打包成可執(zhí)行的jar包,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java Document生成和解析XML操作

    Java Document生成和解析XML操作

    這篇文章主要介紹了Java Document生成和解析XML操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java正則表達(dá)式易錯(cuò)知識(shí)點(diǎn)匯總

    Java正則表達(dá)式易錯(cuò)知識(shí)點(diǎn)匯總

    這篇文章主要總結(jié)Java正則表達(dá)式易錯(cuò)知識(shí),對(duì)易錯(cuò)知識(shí)點(diǎn)進(jìn)行分類(lèi)整理,幫助大家更好的學(xué)習(xí)Java正則表達(dá)式,感興趣的小伙伴們可以參考一下
    2015-12-12
  • java 類(lèi)加載機(jī)制和反射詳解及實(shí)例代碼

    java 類(lèi)加載機(jī)制和反射詳解及實(shí)例代碼

    這篇文章主要介紹了java 類(lèi)加載機(jī)制和反射詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Springboot多環(huán)境開(kāi)發(fā)及使用方法

    Springboot多環(huán)境開(kāi)發(fā)及使用方法

    這篇文章主要介紹了Springboot多環(huán)境開(kāi)發(fā)及多環(huán)境設(shè)置使用、多環(huán)境分組管理的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • Eclipse中創(chuàng)建Web項(xiàng)目最新方法(2023年)

    Eclipse中創(chuàng)建Web項(xiàng)目最新方法(2023年)

    在Java開(kāi)發(fā)人員中,最常用的開(kāi)發(fā)工具應(yīng)該就是Eclipse,下面這篇文章主要給大家介紹了關(guān)于Eclipse中創(chuàng)建Web項(xiàng)目2023年最新的方法,需要的朋友可以參考下
    2023-09-09
  • Java基礎(chǔ)之static的用法

    Java基礎(chǔ)之static的用法

    這篇文章主要介紹了Java基礎(chǔ)之static的用法,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很大的幫助,需要的朋友可以參考下
    2021-05-05
  • Java項(xiàng)目打包發(fā)布到maven私倉(cāng)常見(jiàn)的幾種方式

    Java項(xiàng)目打包發(fā)布到maven私倉(cāng)常見(jiàn)的幾種方式

    這篇文章主要介紹了項(xiàng)目打包發(fā)布到maven私倉(cāng)常見(jiàn)的幾種方式,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-03-03
  • java之構(gòu)造器的重載問(wèn)題

    java之構(gòu)造器的重載問(wèn)題

    這篇文章主要介紹了java之構(gòu)造器的重載問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03

最新評(píng)論