【SpringCloud】分布式事务-TX-LCN(六)

文章目录

  • 前言
  • Note
  • Core-Code(TX-LCN-MANAGER)
    • maven
    • application.yml
    • Code
  • Core-code(TX-LCN-CLIENT)
    • maven
    • 调用方 consumer
      • Service
      • 关键点
    • 提供方 provider
      • Service - TCC
      • Service - TXC
      • Api提供 (Feign)
      • 关键点
  • GitHub
  • Author

前言

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

Note

Core-Code(TX-LCN-MANAGER)

maven


    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.1.6.RELEASEversion>
    parent>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <java.version>1.8java.version>
        <maven.compiler.source>1.8maven.compiler.source>
        <maven.compiler.target>1.8maven.compiler.target>
        
        <springcloud.version>Greenwich.RELEASEspringcloud.version>
    properties>
<dependencyManagement>
    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-dependenciesartifactId>
            <version>${springcloud.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>

        
        <dependency>
            <groupId>com.codingapi.txlcngroupId>
            <artifactId>txlcn-tmartifactId>
            <version>${txlcn.version}version>
        dependency>

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-consul-discoveryartifactId>
        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;

/**
 * LcnServerApplication
 * 

* Description: SpringCloud LCN-manager服务(分布式事务) 默认是8070端口 *

* Creation Time: 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.txlcngroupId>
            <artifactId>txlcn-tcartifactId>
            <version>${txlcn.version}version>
        dependency>

        <dependency>
            <groupId>com.codingapi.txlcngroupId>
            <artifactId>txlcn-txmsg-nettyartifactId>
            <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;

/**
 * ProductAPI
 * 

* Description: *

* Creation Time: 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"));
    }
}

GitHub

不同版本对应不同分支

springcloud-greenwich采用了consul-config,不需要下方的hui-base-springcloud-config-repo

【SpringCloud】完整工程 https://github.com/ithuhui/hui-base-springcloud

【Config-Server】配置中心配置文件:https://github.com/ithuhui/hui-base-springcloud-config-repo

Author

 作者:HuHui
 转载:欢迎一起讨论web和大数据问题,转载请注明作者和原文链接,感谢

你可能感兴趣的:(JavaWeb,Developer,Manual)