支付宝分布式事務サービスDTS二

60119 ワード

分散トランザクションサービスDTS 2
より多くの乾物
  • 分布式実戦(乾物)
  • spring cloud実戦(乾物)
  • mybatis実戦(乾物)
  • spring boot実戦(乾物)
  • React入門実戦(乾物)
  • 中小規模インターネット企業アーキテクチャ(乾物)
  • の構築
    DTSをどのように遊ぶかは、基本的にDTSを使用して発起人の構成要件が少し多くなります.
    DTS依存の追加
    NOTE:イニシエータも参加者も依存を追加する必要があります.
    SOFA Liteを使用する場合は、サンプルエンジニアリングに依存を追加するだけです.
    <dependency>
        <groupId>com.alipay.sofagroupId>
        <artifactId>slite-starter-xtsartifactId>
    dependency>

    SOFA Liteが使用されていない場合、pom構成にDTS依存性を追加する必要があります.
    <dependency>
        <groupId>com.alipay.xtsgroupId>
        <artifactId>xts-coreartifactId>
        <version>6.0.8version>
    dependency>
    <dependency>
        <groupId>com.alipay.xtsgroupId>
        <artifactId>xts-adapter-sofaartifactId>
        <version>6.0.8version>
    dependency>

    シーン紹介
  • まず、振り込みサービスは、銀行Aのある口座から銀行Bのある口座に100元振り込まれ、銀行Aと銀行Bは2つの個別のシステム、すなわち2つの個別のデータベースと考えることができる.
  • アカウントシステムをアカウントと残高の2つのフィールドのみに簡略化し、DTSの2段階設計要求に適応するために、業務上凍結金額を追加しました.(凍結金額とは、1つの振替期間中に、1段階のときにこのフィールドを使用して振替金額を一時的に格納することであり、その振替額は使用できません.この分散トランザクションがすべて送信されたときにのみ、実際に使用可能な残高に計上されます).このような設計によれば、ユーザの利用可能残高は、口座残高から凍結金額を差し引くことに等しい.これは、参加者の設計を理解する鍵であり、DTSが最終的に一致するビジネス制約を保証することでもある.
  • 同時に口座の操作明細を記録するために、口座の操作明細を記録するために口座流水表を設計したので、分野の対象は以下のように簡単に設計された:
  • public class Account {
        /**
         *   
         */
        private String accountNo;
        /**
         *   
         */
        private double amount;
        /**
         *     
         */
        private double freezedAmount;
    public class AccountTransaction {
        /**
         *   id
         */
        private String txId;
        /**
         *     
         */
        private String accountNo;
        /**
         *     
         */
        private double amount;
        /**
         *     ,      
         */
        private String type;

    A銀行の参加者
    私たちはA口座から100元を差し引く必要があると仮定して、Aシステムは1つの差し引きのサービスを提供して、対応する差し引きの1段階のインターフェースと対応する2段階のインターフェースは以下の通りです:
    /**
     * A     ,      
     * @version $Id: FirstAction.java, v 0.1 2014 9 22    5:32:59 Exp $
     */
    public interface FirstAction {
      /**
       *      ,     xts    
       * 
       * @param businessActionContext
       * @param accountNo
       * @param amount
       */
      @TwoPhaseBusinessAction(name = "firstAction", commitMethod = "commit", rollbackMethod = "rollback")
      public void prepare_minus(BusinessActionContext businessActionContext,String accountNo,double amount);
      /**
       *         
       * @param businessActionContext
       * @return
       */
      public boolean commit(BusinessActionContext businessActionContext);
      /**
       *         
       * @param businessActionContext
       * @return
       */
      public boolean rollback(BusinessActionContext businessActionContext);
    }

    対応するフェーズ控除の実装
    public void prepare_minus(final BusinessActionContext businessActionContext,
                              final String accountNo, final double amount) {
        transactionTemplate.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                try {
                    try {
                            //    
                            Account account = accountDAO.getAccount(accountNo);
                            if (account.getAmount() - amount < 0) {
                                throw new TransactionFailException("    ");
                            }
                            //          
                            AccountTransaction accountTransaction = new AccountTransaction();
                            accountTransaction.setTxId(businessActionContext.getTxId());
                            accountTransaction.setAccountNo(accountNo);
                            accountTransaction.setAmount(amount);
                            accountTransaction.setType("minus");
                            //    ,        C  ,         
                            accountTransaction.setStatus("I");
                            accountTransactionDAO.addTransaction(accountTransaction);
                            //       ,           ,    
                            double freezedAmount = account.getFreezedAmount() + amount;
                            account.setFreezedAmount(freezedAmount);
                            accountDAO.updateFreezedAmount(account);
                        } catch (Exception e) {
                            System.out.println("     ," + e);
                            throw new TransactionFailException("       ", e);
                        }
                return null;
            }
        });
    }

    対応する2段階コミット操作
    public boolean commit(final BusinessActionContext businessActionContext) {
        transactionTemplate.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                try {
                        //        
                        AccountTransaction accountTransaction = accountTransactionDAO
                            .findTransaction(businessActionContext.getTxId());
                        //        
                        if (accountTransaction == null) {
                            throw new TransactionFailException("       ");
                        }
                        //            
                        if (StringUtils.equalsIgnoreCase("C", accountTransaction.getStatus())) {
                            return true;
                        }
                        Account account = accountDAO.getAccount(accountTransaction.getAccountNo());
                        //  
                        double amount = account.getAmount() - accountTransaction.getAmount();
                        if (amount < 0) {
                            throw new TransactionFailException("    ");
                        }
                        account.setAmount(amount);
                        accountDAO.updateAmount(account);
                        //        
                        account.setFreezedAmount(account.getFreezedAmount()
                                                 - accountTransaction.getAmount());
                        accountDAO.updateFreezedAmount(account);
                        //         C
                        accountTransactionDAO.updateTransaction(businessActionContext.getTxId(), "C");
                    } catch (Exception e) {
                        System.out.println("     ," + e);
                        throw new TransactionFailException("       ", e);
                    }
                return null;
            }
        });
        return false;
    }

    対応する2段階ロールバック操作
    public boolean rollback(final BusinessActionContext businessActionContext) {
        transactionTemplate.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                try {
                        //      
                        AccountTransaction accountTransaction = accountTransactionDAO
                            .findTransaction(businessActionContext.getTxId());
                        if (accountTransaction == null) {
                            System.out.println("   ---     ");
                            return null;
                        }
                        Account account = accountDAO.getAccount(accountTransaction.getAccountNo());
                        account.setFreezedAmount(account.getFreezedAmount()
                                                 - accountTransaction.getAmount());
                        accountDAO.updateFreezedAmount(account);
                        //    
                        accountTransactionDAO.deleteTransaction(businessActionContext.getTxId());
                    } catch (Exception e) {
                        System.out.println("     ," + e);
                        throw new TransactionFailException("       ", e);
                    }
                  return null;
            }
       });
       return false;
    }

    B銀行の参加者
    私たちはB口座に100元を入金する必要があると仮定して、Bシステムは入金に対応する1段階インタフェースと対応する2段階インタフェースが基本的にA銀行の参加者と似ているサービスを提供して、ここではあまり紹介しないで、サンプル工事の下のxts-sample工事コードを直接見ることができます.
    発起人
    参加者の実装の詳細について説明したが、次に、発起人システムがこの2人の参加者をどのように調整し、分散トランザクションの下でデータの最終的な一貫性を達成するかを見てみましょう.参加者に比べて、発起人の構成は複雑です.
  • 発起人自身のデータベースにDTSを作成するテーブル
  • BusinessActivity Control
  • の構成
    BusinessActivityControlServiceはDTS分散トランザクションの開始クラスであり、SOFA環境ではこのように使用できます.
    <!---->
    <sofa:xts id="businessActivityControlService">
      <!--          ,    zdal     ,      dbcp    -->
       <sofa:datasource ref="activityDataSource"/>
      <!--     zdal   ,          ,  dbType           ,   xts  sqlmap -->
       <sofa:dbtype value="mysql"/>
    </sofa:xts>

    他の環境では、次のように構成された通常のBeanに構成することもできます.
    <!---->
    <bean name="businessActivityControlService" class="com.alipay.xts.client.api.impl.sofa.BusinessActivityControlServiceImplSofa">
       <!--          ,    zdal     ,      dbcp    -->
       <property name="dataSource" ref="activityDataSource"/>
       <!--     zdal   ,          ,  dbType           ,   xts  sqlmap -->
       <property name="dbType" value="mysql"/>
    </bean>
  • 参加者サービスとブロッキングを構成します.SOFA環境の場合、DTSフレームワークは参加者メソッドを自動的にブロックします.ブロッキングは
  • を構成する必要はありません.
    <!--           -->
    <bean id="firstAction" class="org.springframework.aop.framework.ProxyFactoryBean">
       <property name="proxyInterfaces" value="com.alipay.xts.client.sample.action.FirstAction"/>
       <property name="target" ref="firstActionTarget"/>
       <property name="interceptorNames">
          <list>
              <value>businessActionInterceptor</value>
          </list>
       </property>
    </bean>
    <!--        -->
    <bean id="firstActionTarget" class="com.alipay.xts.client.sample.action.impl.FirstActionImpl">
       <property name="accountTransactionDAO">
          <ref bean="firstActionAccountTransactionDAO" />
       </property>
       <property name="accountDAO">
          <ref bean="firstActionAccountDAO" />
       </property>
       <property name="transactionTemplate">
          <ref bean="firstActionTransactionTemplate" />
       </property>
    </bean>
    <!--           -->
    <bean id="secondAction" class="org.springframework.aop.framework.ProxyFactoryBean">
       <property name="proxyInterfaces" value="com.alipay.xts.client.sample.action.SecondAction"/>
       <property name="target" ref="secondActionTarget"/>
       <property name="interceptorNames">
         <list>
            <value>businessActionInterceptor</value>
         </list>
       </property>
    </bean>
    <!--        -->
    <bean id="secondActionTarget" class="com.alipay.xts.client.sample.action.impl.SecondActionImpl">
       <property name="accountTransactionDAO">
          <ref bean="secondActionAccountTransactionDAO" />
       </property>
       <property name="accountDAO">
          <ref bean="secondActionAccountDAO" />
       </property>
       <property name="transactionTemplate">
          <ref bean="secondActionTransactionTemplate" />
       </property>
    </bean>
    <!--    ,         ,      action   -->
    <bean id="businessActionInterceptor"
         class="com.alipay.sofa.platform.xts.bacs.integration.BusinessActionInterceptor">
       <property name="businessActivityControlService" ref="businessActivityControlService"/>
    </bean>
  • 分散トランザクション
  • を開始
    分散トランザクションの開始のためのエントリ・メソッド
    /**
     *         。
     * 
     *             ,    businessType businessId,         。
     * 
     *         start        ,            ,               ,                 。
     * 
     * @param businessType     ,        ,  'trade_pay'      
     * @param businessId    ,    
     * @notice        : businessType+"-"+businessId,    128
     * @return 
     */
    BusinessActivityId start(String businessType, String businessId, Map<String, Object> properties);

    BusinessType+businessIdは最終的なトランザクション番号であり、propertiesは発起人にグローバルなトランザクションコンテキスト情報を設定させることができます.
    振替サービスによる分散トランザクションの開始
    /**
     *       
     * 
     * @param from
     * @param to
     * @param amount
     */
    public void transfer(final String from, final String to, final double amount) {
        /**
         *   :  xts                  
         */
        transactionTemplate.execute(new TransactionCallback() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
               System.out.println("    xts       ");
                    //
                    Map<String, Object> properties = new HashMap<String, Object>();
                    BusinessActivityId businessActivityId = businessActivityControlService.start("pay",
                        businessId, properties);
                    System.out.println("=====         ,   :" + businessActivityId.toStringForm()
                                       + "=====");
                    System.out.println("=====   ,   B        =====");
                    //          
                    if (secondAction.prepare_add(null, to, amount)) {
                        System.out.println("=====   , B          =====");
                    } else {
                        System.out.println("=====   , B          ,    =====");
                        status.setRollbackOnly();
                        return null;
                    }
                    System.out.println("=====   ,   A        =====");
                    //          
                    if (firstAction.prepare_minus(null, from, amount)) {
                        System.out.println("=====   , A          =====");
                    } else {
                        System.out.println("=====   , A          ,    =====");
                        status.setRollbackOnly();
                    }
                return null;
            }
        });
        System.out.println("   ----    ,    ");
    }

    小結
    DTS開発を使用する上で注目すべきことは、上記の内容です.参加者にとって最も重要なのは、ビジネス上で最終的な一貫性を保証するためにどのように2段階の処理を実現するかです.発起人にとって、主にDTSのテーブルを構成することです.
    参考資料
    SOFAフレームワーク.pptx