文章目录
Note
- 下面以springcloud-greenwich分支做demo
- 官方的github写的很详细很好,我的demo跟它的一致(不理解为什么网上很多人说demo有问题。我跑起来一点问题没有,而且比较清晰)
- 两种模式包括: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");
}
}
关键点
- 要么使用AOP的方式。等于帮你在方法上面自动加注解
- 或者直接使用声明式方法,手动加注解
- 这里演示的跟官方文档写的一致。调用方使用了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;
}
}
}
关键点
- TCC补偿模式需要同时写回滚的方法和确认方法,因此DEMO必须多写两个方法cancelTccAdd&confirmTccAdd
- FallBack容错下执行DTXUserControls.rollbackGroup(TracingContext.tracing().groupId())
- LCN-TCC模式通过一个唯一的KEY值,用于确认/回滚,因此需要提供PrimaryKeysProvider。详细看下方代码
- 这里演示的跟官方文档写的一致,只是修改了对应表名和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和大数据问题,转载请注明作者和原文链接,感谢