最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Seata AT模式TransactionHook被刪除探究

 更新時(shí)間:2022年11月15日 10:25:06   作者:夢想實(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)問題所在:

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

問題定位

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

try {
    // 開啟分布式事務(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)場
    resumeGlobalLockConfig(previousConfig);
    triggerAfterCompletion();
    cleanUp();
}

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

public final class TransactionHookManager {
  private static final ThreadLocal<List<TransactionHook>> LOCAL_HOOKS = new ThreadLocal<>();
  // 注冊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都清除掉了。也就是說,假如事務(wù)A和事務(wù)B共用同一個(gè)線程,當(dāng)事務(wù)B處理完畢后,調(diào)用了cleanUp()回收現(xiàn)場時(shí),把該線程當(dāng)中存儲(chǔ)的所有TransactionHook全部清除掉了,導(dǎo)致事務(wù)A的生命周期中找不到該事務(wù)對應(yīng)的TransactionHook,從而產(chǎn)生了BUG。

如何解決

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

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

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

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

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

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

    private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
        try {
            // 執(zhí)行triggerBeforeBegin()
            triggerBeforeBegin();
            // 注冊分布式事務(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è)問題,因?yàn)槲覀兊?code>TransactionHook依賴于XID,但是triggerBeforeBegin()執(zhí)行的時(shí)候還沒有產(chǎn)生XID,所以為了能夠在沒有XID的時(shí)候也能夠讓TransactionHook生效,我們要有一個(gè)虛值key來臨時(shí)設(shè)置TransactionHook

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

注意事項(xiàng)

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

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);
        }

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

修改后的代碼如下:

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的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IDEA編譯時(shí)報(bào)常量字符串過長的解決辦法

    IDEA編譯時(shí)報(bào)常量字符串過長的解決辦法

    本文主要介紹了IDEA編譯時(shí)報(bào)常量字符串過長的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • SpringBoot中最常用的5個(gè)內(nèi)置對象使用方法

    SpringBoot中最常用的5個(gè)內(nèi)置對象使用方法

    這篇文章主要給大家介紹了關(guān)于SpringBoot中最常用的5個(gè)內(nèi)置對象使用的相關(guān)資料,在學(xué)習(xí)springboot的過程中,發(fā)現(xiàn)了springboot非常多的優(yōu)點(diǎn)和特性,需要的朋友可以參考下
    2023-08-08
  • spring+mybatis 通過@ResponseBody返回結(jié)果中文亂碼的解決方法

    spring+mybatis 通過@ResponseBody返回結(jié)果中文亂碼的解決方法

    下面小編就為大家分享一篇spring+mybatis 通過@ResponseBody返回結(jié)果中文亂碼的解決方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • Spring遠(yuǎn)程加載配置的實(shí)現(xiàn)方法詳解

    Spring遠(yuǎn)程加載配置的實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了Spring遠(yuǎn)程加載配置的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-03-03
  • springboot?vue接口測試HutoolUtil?TreeUtil處理樹形結(jié)構(gòu)

    springboot?vue接口測試HutoolUtil?TreeUtil處理樹形結(jié)構(gòu)

    這篇文章主要介紹了springboot?vue接口測試HutoolUtil?TreeUtil處理樹形結(jié)構(gòu),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • mybatisplus添加真正的批量新增、批量更新的實(shí)現(xiàn)

    mybatisplus添加真正的批量新增、批量更新的實(shí)現(xiàn)

    這篇文章主要介紹了mybatisplus添加真正的批量新增、批量更新的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 解決java調(diào)用python代碼返回值中文亂碼問題

    解決java調(diào)用python代碼返回值中文亂碼問題

    這篇文章主要介紹了解決java調(diào)用python代碼返回值中文亂碼問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 淺談SpringBoot在使用測試的時(shí)候是否需要@RunWith

    淺談SpringBoot在使用測試的時(shí)候是否需要@RunWith

    本文主要介紹了淺談SpringBoot在使用測試的時(shí)候是否需要@RunWith,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Socket結(jié)合線程池使用實(shí)現(xiàn)客戶端和服務(wù)端通信demo

    Socket結(jié)合線程池使用實(shí)現(xiàn)客戶端和服務(wù)端通信demo

    這篇文章主要為大家介紹了Socket結(jié)合線程池的使用來實(shí)現(xiàn)客戶端和服務(wù)端通信實(shí)戰(zhàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03
  • JVM之參數(shù)分配(全面講解)

    JVM之參數(shù)分配(全面講解)

    下面小編就為大家?guī)硪黄狫VM之參數(shù)分配(全面講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05

最新評論

大名县| 西平县| 巨鹿县| 启东市| 栖霞市| 铁岭县| 奉贤区| 张家港市| 七台河市| 寿阳县| 张掖市| 兴化市| 桐乡市| 禄劝| 平凉市| 新龙县| 永康市| 望奎县| 读书| 台中市| 金阳县| 饶阳县| 衡阳县| 梅河口市| 盈江县| 龙州县| 延津县| 内乡县| 来安县| 普安县| 武清区| 驻马店市| 霍山县| 怀化市| 汽车| 静安区| 博兴县| 吐鲁番市| 柳河县| 义乌市| 岑溪市|