1. 程式人生 > >tcc-transaction 1.2.x 使用指南

tcc-transaction 1.2.x 使用指南

1.2.x支援rpc框架支援隱式傳參情況下將事務上下文引數TransactionContext以隱式傳參方式進行傳遞,目前支援rpc框架為dubbo時的隱式傳參方式傳遞,具體配置可參考下面“rpc框架為dubbo時隱式傳參方式配置”。

配置tcc-transaction

1 引用tcc-transaction

在服務呼叫方和提供方專案中需要引用tcc-transaction-spring jar包,如使用maven依賴:

    <dependency>
        <groupId>org.mengyun</groupId>
        <artifactId>tcc-transaction-spring</artifactId>
        <version>${project.version}</version>
    </dependency>

2 載入tcc-transaction.xml配置

啟動應用時,需要將tcc-transaction-spring jar中的tcc-transaction.xml加入到classpath中。如在web.xml中配置:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:tcc-transaction.xml
    </param-value>
</context-param>

3 設定TransactionRepository

需要為參與事務的應用專案配置一個TransactionRepository,tcc-transaction框架使用transactionRepository持久化事務日誌。可以選擇FileSystemTransactionRepository、SpringJdbcTransactionRepository、RedisTransactionRepository或ZooKeeperTransactionRepository。

使用SpringJdbcTransactionRepository配置示例如下:

<bean id="transactionRepository"
      class="org.mengyun.tcctransaction.spring.repository.SpringJdbcTransactionRepository">
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
      destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value=""/>
</bean>

特別提示,dataSource需要單獨配置,不能和業務裡使用的dataSource複用,即使使用的是同一個資料庫。

使用RedisTransactionRepository配置示例如下(需配置redis伺服器為AOF模式並在redis.conf中設定appendfsync為always以防止日誌丟失):

<bean id="transactionRepository" class="org.mengyun.tcctransaction.repository.RedisTransactionRepository">
<property name="keyPrefix" value="tcc_ut_"/>
<property name="jedisPool" ref="jedisPool"/>
</bean>

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="1000"/>
<property name="maxWaitMillis" value="1000"/>
</bean>

<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg index="0" ref="jedisPoolConfig"/>
<constructor-arg index="1" value="127.0.0.1"/>
<constructor-arg index="2" value="6379" type="int"/>
<constructor-arg index="3" value="1000" type="int"/>
<!--<constructor-arg index="4" value="${redis.password}"/>-->
</bean>

使用ZooKeeperTransactionRepository配置示例如下:

<bean id="transactionRepository"
class="org.mengyun.tcctransaction.repository.ZooKeeperTransactionRepository">
<!--<property name="zkServers" value="localhost:2181,localhost:2183,localhost:2185"/>-->
<property name="zkServers" value="localhost:2181"/>
<property name="zkTimeout" value="10000"/>
<property name="zkRootPath" value="/tcc_ut"/>
</bean>

使用FileSystemTransactionRepository配置如下(FileSystemTransactionRepository僅適用事務釋出方或呼叫方應用節點為單節點場景,因為日誌是儲存在應用節點本地檔案中):

<bean id="transactionRepository" class="org.mengyun.tcctransaction.repository.FileSystemTransactionRepository">
<property name="rootPath" value="/data/tcc"/>
</bean>

4 設定恢復策略(可選)

當Tcc事務異常後,恢復Job將會定期恢復事務。在Spring配置檔案中配置RecoverConfig型別的Bean來設定恢復策略示例:

<bean class="org.mengyun.tcctransaction.spring.recover.DefaultRecoverConfig">
    <property name="maxRetryCount" value="30"/>
    <property name="recoverDuration" value="120"/>
    <property name="cronExpression" value="0 */1 * * * ?"/>
    <property name="delayCancelExceptions">
        <util:set>
            <value>com.alibaba.dubbo.remoting.TimeoutException</value>
        </util:set>
    </property>
</bean>

其中maxRetryCount表示一個事務最多嘗試恢復次數,超過將不再自動恢復,需要人工干預,預設是30次。

recoverDuration表示一個事務日誌當超過一定時間間隔後沒有更新就會被認為是發生了異常,需要恢復,恢復Job將掃描超過這個時間間隔依舊沒有更新的事務日誌,並對這些事務進行恢復,時間單位是秒,預設是120秒。

cronExpression表示恢復Job觸發間隔配置,預設是0 */1 * * * ?。

delayCancelExceptions(1.2.3版中新加的配置)表示系統發生了設定的異常時,主事務不立即rollback,而是由恢復job來執行事務恢復。通常需要將超時異常設定為delayCancelExceptions,這樣可以避免因為服務呼叫時發生了超時異常,主事務如果立刻rollback, 但是從事務還沒執行完,從而造成主事務rollback失敗。示例中com.alibaba.dubbo.remoting.TimeoutException為底層rpc框架為dubbo,超時異常發生時框架丟擲的超時異常類,需要將其加入delayCancelExceptions中。

釋出Tcc服務

釋出一個Tcc服務方法,可被遠端呼叫並參與到Tcc事務中,釋出Tcc服務方法有下面四個約束:

  1. 在服務提供方的實現方法上加上@Compensable註解,並設定註解的屬性
  2. 服務方法第一個入參型別為org.mengyun.tcctransaction.api.TransactionContext
  3. 服務方法的入參能被序列化(預設使用jdk序列化機制,需要引數實現Serializable介面,可以設定repository的serializer屬性自定義序列化實現)
  4. try方法、confirm方法和cancel方法入參型別須一樣

Compensable的屬性包括propagation、confirmMethod、cancelMethod、transactionContextEditor。propagation可不用設定,框架使用預設值;設定confirmMethod指定CONFIRM階段的呼叫方法;設定cancelMethod指定CANCEL階段的呼叫方法;設定transactionContextEditor為MethodTransactionContextEditor.class。與1.1.x版本不同,從1.2.x起,TransactionContext將提供可配置的傳遞方式,不再要求服務方法第一個org.mengyun.tcctransaction.api.TransactionContext型別的入參為預留入參,而是可以自定義邏輯將TransactionContext以顯示或隱式傳參方式在呼叫方和服務提供方間傳遞。tcc-transaction框架使用transactionContextEditor來實現獲取和呼叫方向服務提供方傳遞TransactionContext。如果底層服務框架使用的是dubbo,可以設定transactionContextEditor為DubboTransactionContextEditor.class(在tcc-transaction-dubbo.jar中,使用dubbo隱式傳參方式),如果TransactionContext通過服務提供方方法引數形式傳遞(正如1.1.x版實現),則可設定transactionContextEditor為MethodTransactionContextEditor.class(在tcc-transaction-core.jar中)。

tcc-transaction將攔截加上了@Compensable註解的服務方法,並根據Compensalbe的confirmMethod和cancelMethod獲取在CONFRIM階段和CANCEL階段需要呼叫的方法。註解屬性tcc-transaction在呼叫confirmMethod或是cancelMethod時是根據釋出Tcc服務的介面類在Spring的ApplicationContext中獲取Tcc服務例項,並呼叫confirmMethod或cancelMethod指定方法。因此如果是使用動態代理的方式實現aop(預設方式),則confirmMethod和cancelMethod需在介面類中宣告,如果使用動態位元組碼技術實現aop(如指定aspectj-autoproxy的proxy-target-class屬性為true,在1.2.1版本中,預設已設定為true),則無需在介面類中宣告。

tcc-transaction在執行服務過程中會將Tcc服務的上下文持久化,包括所有入參,內部預設實現為將入參使用jdk自帶的序列化機制序列化為為byte流,所以需要實現Serializable介面(另外可選擇Kryo序列化實現機制,可參考例子)。

在tcc-transaction-http-capital中釋出Tcc服務示例:

try介面方法:

 public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto);

try實現方法:

@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class)
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {

confirm方法:

public void confirmRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {

cancel方法:

public void cancelRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {

在tcc-transaction-http-redpacket中釋出Tcc服務示例:

try介面方法:

public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto);

try實現方法:

@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class)
public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {

confirm方法:

public void confirmRecord(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {

cancel方法:

public void cancelRecord(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {

呼叫遠端Tcc服務

呼叫遠端Tcc服務,將遠端Tcc服務參與到本地Tcc事務中,本地的服務方法也需要宣告為Tcc服務,與釋出一個Tcc服務不同,本地Tcc服務方法有三個約束:

  1. 在服務方法上加上@Compensable註解,並設定註解屬性
  2. 服務方法的入參都須能序列化(實現Serializable介面)
  3. try方法、confirm方法和cancel方法入參型別須一樣

與釋出Tcc服務不同的是本地Tcc服務Compensable註解屬性transactionContextEditor可以不用設定。

本地服務通過遠端tcc服務提供的client來呼叫,需要將這些tcc服務的client宣告為可加入到TCC事務中,如tcc服務的client方法明為tryXXX,則需要在方法tryXXX上加上如下配置:

@Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "tryXXX", cancelMethod = "tryXXX", transactionContextEditor = MethodTransactionContextEditor.class)

其中propagation = Propagation.SUPPORTS表示該方法支援參與到TCC事務中。 如果tcc服務的client為框架自動生成實現(比如代理機制實現)不能添加註解,可為該client實現一個代理類,在代理類的方法上加上註解。

在tcc-transaction-http-order中呼叫遠端Tcc服務示例:

try方法:

@Compensable(confirmMethod = "confirmMakePayment",cancelMethod = "cancelMakePayment")
public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {

    System.out.println("order try make payment called");
    order.pay(redPacketPayAmount, capitalPayAmount);
    orderRepository.updateOrder(order);
    capitalTradeOrderService.record(null, buildCapitalTradeOrderDto(order));
    redPacketTradeOrderService.record(null, buildRedPacketTradeOrderDto(order));
}

confirm方法:

public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {

cancel方法:

public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {

tcc服務提供的client方法增加註解:

@Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = MethodTransactionContextEditor.class)
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {

rpc框架為dubbo時以隱式傳參方式配置

rpc框架為dubbo時支援以隱式傳參方式配置TCC事務。

配置tcc-transaction

與上面配置tcc-transaction一樣,此外,還需要如下步驟:

  1. 引用tcc-transaction-dubbo 在專案中需要引用tcc-transaction-dubbo jar包,如使用maven依賴:

     <dependency>
         <groupId>org.mengyun</groupId>
         <artifactId>tcc-transaction-dubbo</artifactId>
         <version>${project.version}</version>
     </dependency>
    
  2. 載入tcc-transaction-dubbo.xml配置

需要將tcc-transaction-dubbo jar中的tcc-transaction-dubbo.xml加入到classpath中。如在web.xml中配置改為:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:tcc-transaction.xml,classpath:tcc-transaction-dubbo.xml
    </param-value>
 </context-param>

釋出Tcc服務

釋出一個Tcc服務方法,可被遠端呼叫並參與到Tcc事務中,釋出支援隱式傳參的Tcc服務方法有下面四個約束:

  1. 在服務提供方的實現方法上加上@Compensable註解,並設定註解的屬性
  2. 在服務提供方的介面方法上加上@Compensable註解
  3. 服務方法的入參能被序列化(預設使用jdk序列化機制,需要引數實現Serializable介面,可以設定repository的serializer屬性自定義序列化實現)
  4. try方法、confirm方法和cancel方法入參型別須一樣

Compensable的屬性包括propagation、confirmMethod、cancelMethod、transactionContextEditor。propagation可不用設定,框架使用預設值;設定confirmMethod指定CONFIRM階段的呼叫方法;設定cancelMethod指定CANCEL階段的呼叫方法;設定transactionContextEditor為DubboTransactionContextEditor.class。

在tcc-transaction-dubbo-capital中釋出Tcc服務示例:

try介面方法:

 @Compensable
public String record(CapitalTradeOrderDto tradeOrderDto);

try實現方法:

@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = DubboTransactionContextEditor.class)
public String record(CapitalTradeOrderDto tradeOrderDto) {

confirm方法:

public void confirmRecord(CapitalTradeOrderDto tradeOrderDto) {

cancel方法:

public void cancelRecord(CapitalTradeOrderDto tradeOrderDto) {

在tcc-transaction-dubbo-redpacket中釋出Tcc服務示例:

try介面方法:

@Compensable
public String record(RedPacketTradeOrderDto tradeOrderDto);

try實現方法:

@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = DubboTransactionContextEditor.class)
public String record(RedPacketTradeOrderDto tradeOrderDto) {

confirm方法:

public void confirmRecord(RedPacketTradeOrderDto tradeOrderDto) {

cancel方法:

public void cancelRecord(RedPacketTradeOrderDto tradeOrderDto) {

呼叫遠端Tcc服務

呼叫遠端Tcc服務,將遠端Tcc服務參與到本地Tcc事務中,本地的服務方法也需要宣告為Tcc服務,宣告方式與非隱式傳參方式一樣,有三個約束:

  1. 在服務方法上加上@Compensable註解,並設定註解屬性
  2. 服務方法的入參都須能序列化(實現Serializable介面)
  3. try方法、confirm方法和cancel方法入參型別須一樣

本地服務通過遠端tcc服務提供的client來呼叫,與非隱式傳參方式不一樣,無需要將這些tcc服務的client顯示地宣告為可加入到TCC事務中。

在tcc-transaction-dubbo-order中呼叫遠端Tcc服務示例:

try方法:

@Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment")
public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
    System.out.println("order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));

    order.pay(redPacketPayAmount, capitalPayAmount);
    orderRepository.updateOrder(order);

    String result = capitalTradeOrderService.record(buildCapitalTradeOrderDto(order));
    String result2 = redPacketTradeOrderService.record(buildRedPacketTradeOrderDto(order));
}

confirm方法:

public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {

cancel方法:

public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {