【SpringCloud】Tx-Lcn-分布式事务(六)

Note

  1. 下面以springcloud-greenwich分支做demo
  2. 官方的github写的很详细很好,我的demo跟它的一致(不理解为什么网上很多人说demo有问题。我跑起来一点问题没有,而且比较清晰)
  3. 两种模式包括:TXC&TCC都经过测试

GitHub

地址: https://github.com/ithuhui/hui-base-springcloud
分支:finchley | greenwich
模块:【hui-base-springcloud-tx-lcn】

Core-Code(TX-LCN-MANAGER)

maven

<!--SpringBoot Parent-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        
        <springcloud.version>Greenwich.RELEASE</springcloud.version>
    </properties>
<dependencyManagement>
    <dependencies>
        <!--Spring Cloud dependencies-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${springcloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

        <!--LCN-->
        <dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-tm</artifactId>
            <version>${txlcn.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

application.yml

server:
  port: 28771
spring:
  application:
    name: tx-lcn-server
  redis:
    host: 192.168.89.4
    port: 6379
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1/tx-manager?characterEncoding=utf-8&useSSL=false
  cloud:
    host: 127.0.0.1
    port: 8500
    consul:
      discovery:
        service-name: tx-lcn-server
        health-check-path: /actuator/health
        health-check-interval: 10s
        prefer-ip-address: true
      config:
        enabled: true
        format: yaml
        data-key: data #表示consul上面的KEY值(或者说文件的名字) 默认是data

Code

package com.hui.base.springcloud.lcn;

import com.codingapi.txlcn.tm.config.EnableTransactionManagerServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * <b><code>LcnServerApplication</code></b>
 * <p/>
 * Description: SpringCloud LCN-manager服务(分布式事务) 默认是8070端口
 * <p/>
 * <b>Creation Time:</b> 2019/4/1 21:15.
 *
 * @author HuWeihui
 */
@EnableTransactionManagerServer
@SpringBootApplication
@EnableDiscoveryClient
public class LcnServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(LcnServerApplication.class,args);
    }
}


Core-code(TX-LCN-CLIENT)

maven

        <dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-tc</artifactId>
            <version>${txlcn.version}</version>
        </dependency>

        <dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-txmsg-netty</artifactId>
            <version>${txlcn.version}</version>
        </dependency>

调用方 consumer

Service

    /**
     * 测试LCN-TCC
     * @param order
     * @param exFlag
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void testTCC(Order order, String exFlag) {

        // use the tcc mode to start remote transaction
        ProductDTO productDTO = new ProductDTO();
        double ceil = Math.ceil(Math.random() * 100);
        String productId = String.valueOf(ceil);
        productDTO.setProductName("tccTest");
        productDTO.setProductId(productId);
        productFeignApi.tccAdd(productDTO);


        // use the local transaction
        orderMapper.insertSelective(order);

        if (exFlag.equals("true")){
            throw new RuntimeException("test txc exception");
        }

    }

    /**
     * 测试LCN-TXC
     * @param order
     * @param exFlag
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void testTXC(Order order, String exFlag) {

        // use the local transaction
        orderMapper.insertSelective(order);

        // use the txc mode to start remote transaction
        ProductDTO productDTO = new ProductDTO();
        productDTO.setProductName("txcTest");
        double ceil = Math.ceil(Math.random() * 100);
        String productId = String.valueOf(ceil);

        productDTO.setProductId(productId);
        productFeignApi.txcAdd(productDTO);

        if (exFlag.equals("true")){
            throw new RuntimeException("test txc exception");
        }
    }

关键点

  1. 要么使用AOP的方式。等于帮你在方法上面自动加注解
  2. 或者直接使用声明式方法,手动加注解
  3. 这里演示的跟官方文档写的一致。调用方使用了AOP形式
package com.hui.base.springcloud.order.config;

import com.codingapi.txlcn.common.util.Transactions;
import com.codingapi.txlcn.tc.aspect.interceptor.TxLcnInterceptor;
import com.codingapi.txlcn.tc.aspect.weave.DTXLogicWeaver;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import java.util.Properties;

/**
 * Description: AOP方式声明分布式事务示例。product-server 用的注解方式,注意区分,非必须如此配置,可以注解,也可以声明式
 * Date: 19-1-13 下午2:46
 *
 * @author ujued
 */
@Configuration
@EnableTransactionManagement
public class AopTypeDTXConfiguration {

    /**
     * 本地事务配置
     *
     * @param transactionManager
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public TransactionInterceptor transactionInterceptor(PlatformTransactionManager transactionManager) {
        Properties properties = new Properties();
        properties.setProperty("*", "PROPAGATION_REQUIRED,-Throwable");
        TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
        transactionInterceptor.setTransactionManager(transactionManager);
        transactionInterceptor.setTransactionAttributes(properties);
        return transactionInterceptor;
    }

    /**
     * 分布式事务配置 设置为LCN模式
     *
     * @param dtxLogicWeaver
     * @return
     */
    @ConditionalOnBean(DTXLogicWeaver.class)
    @Bean
    public TxLcnInterceptor txLcnInterceptor(DTXLogicWeaver dtxLogicWeaver) {
        TxLcnInterceptor txLcnInterceptor = new TxLcnInterceptor(dtxLogicWeaver);
        Properties properties = new Properties();
        properties.setProperty(Transactions.DTX_TYPE, Transactions.LCN);
        properties.setProperty(Transactions.DTX_PROPAGATION, "REQUIRED");
        txLcnInterceptor.setTransactionAttributes(properties);
        return txLcnInterceptor;
    }

    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setInterceptorNames("txLcnInterceptor", "transactionInterceptor");
        beanNameAutoProxyCreator.setBeanNames("*Impl");
        return beanNameAutoProxyCreator;
    }
}

提供方 provider

Service - TCC

    @Override
    @TccTransaction(propagation = DTXPropagation.SUPPORTS)
    @Transactional(rollbackFor = Exception.class)
    public void tccAdd(Product product) {
        productMapper.insertSelective(product);

        ids.putIfAbsent(TracingContext.tracing().groupId(), Sets.newHashSet(product.getProductId()));
        ids.get(TracingContext.tracing().groupId()).add(product.getProductId());
    }

    public void confirmTccAdd (Product product) {
        ids.get(TracingContext.tracing().groupId()).forEach(id -> {
            log.info("tcc-confirm-{}-{}" , TracingContext.tracing().groupId(), id);
            ids.get(TracingContext.tracing().groupId()).remove(id);
        });
    }

    public void cancelTccAdd(Product product) {
        ids.get(TracingContext.tracing().groupId()).forEach(id -> {
            log.info("tcc-cancel-{}-{}", TracingContext.tracing().groupId(), id);
            productMapper.deleteByPrimaryKey(id);
        });
    }

Service - TXC

    @Override
    @TxcTransaction(propagation = DTXPropagation.SUPPORTS)
    @Transactional(rollbackFor = Exception.class)
    public void txcAdd(Product product) {
        productMapper.insertSelective(product);
    }

Api提供 (Feign)

package com.hui.base.springcloud.product.api;

import com.codingapi.txlcn.tc.support.DTXUserControls;
import com.codingapi.txlcn.tracing.TracingContext;
import com.hui.base.springcloud.common.response.ResultVO;
import dto.ProductDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * <b><code>ProductAPI</code></b>
 * <p/>
 * Description:
 * <p/>
 * <b>Creation Time:</b> 2018/11/28 23:20.
 *
 * @author Hu Weihui
 */
@FeignClient(name = "product-server",path = "/product",fallback = ProductFeignApi.ProductFeignApiFallback.class )//如果产生服务降级就返回NULL
public interface ProductFeignApi {

    @PutMapping("/lcn/tcc/products")
    ResultVO tccAdd(@RequestBody ProductDTO productDTO);

    @PutMapping("/lcn/txc/products")
    ResultVO txcAdd(@RequestBody ProductDTO productDTO);

    @Component
    static class ProductFeignApiFallback implements ProductFeignApi{
        @Override
        public ResultVO tccAdd(ProductDTO productDTO) {
            //TCC的关键点
            DTXUserControls.rollbackGroup(TracingContext.tracing().groupId());
            return null;
        }
        
        @Override
        public ResultVO txcAdd(ProductDTO productDTO) {
            return null;
        }
    }
}

关键点

  1. TCC补偿模式需要同时写回滚的方法和确认方法,因此DEMO必须多写两个方法cancelTccAdd&confirmTccAdd
  2. FallBack容错下执行DTXUserControls.rollbackGroup(TracingContext.tracing().groupId())
  3. LCN-TCC模式通过一个唯一的KEY值,用于确认/回滚,因此需要提供PrimaryKeysProvider。详细看下方代码
  4. 这里演示的跟官方文档写的一致,只是修改了对应表名和PrimaryKeys
package com.hui.base.springcloud.product.config;

import com.codingapi.txlcn.common.util.Maps;
import com.codingapi.txlcn.tc.core.transaction.txc.analy.def.PrimaryKeysProvider;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Description:
 * Date: 19-1-25 下午4:29
 * @author ujued
 */
@Component
public class MysqlPrimaryKeysProvider implements PrimaryKeysProvider {
    @Override
    public Map<String, List<String>> provide() {
        return Maps.of("t_product", Collections.singletonList("product_id"),
                "t_order",Collections.singletonList("order_id"));
    }
}

Author

 作者:HuHui
 转载:欢迎一起讨论web和大数据问题,转载请注明作者和原文链接,感谢
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值