创建一个项目Tm 项目
导包pom
<!-- 参照例子引入需要的依赖jar -->
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
注意 jdbc 包必须依赖 不然启动报错
配置文件
spring.application.name=txlcn-tm
server.port=7970
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.use-generated-keys=true
# TM后台登陆密码
tx-lcn.manager.admin-key=123456
tx-lcn.manager.host=127.0.0.1
tx-lcn.manager.port=8070
# 开启日志,默认为false
tx-lcn.logger.enabled=true
tx-lcn.logger.driver-class-name=${spring.datasource.driver-class-name}
tx-lcn.logger.jdbc-url=${spring.datasource.url}
tx-lcn.logger.username=${spring.datasource.username}
tx-lcn.logger.password=${spring.datasource.password}
logging.level.com.codingapi.txlcn=DEBUG
#redis 主机
spring.redis.host=127.0.0.1
#redis 端口
spring.redis.port=6379
#redis 密码
spring.redis.password=123456
启动类注解
import com.codingapi.txlcn.tm.config.EnableTransactionManagerServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Created by IntelliJ IDEA.
*
* @Author : WangCong
* @create 2020/5/9 15:23
*/
@SpringBootApplication
@EnableTransactionManagerServer
public class TxLcnTmApplication {
public static void main(String[] args) {
SpringApplication.run(TxLcnTmApplication.class,args);
}
}
创建数据库表
然后创建表
CREATE TABLE `t_tx_exception` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`transaction_state` tinyint(4) NULL DEFAULT NULL,
`registrar` tinyint(4) NULL DEFAULT NULL,
`remark` varchar(4096) NULL DEFAULT NULL,
`ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决',
`create_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
这个Tm 服务就弄好了,启动。
在其他 要用到分布式事务的项目 中 引入包
比如我 一个提供者 项目和一个消费者项目 都要引入
这是提供者的pom
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
启动类
/**
* Created by IntelliJ IDEA.
*
* @Author : WangCong
* @create 2020/5/7 15:26
*/
//注册服务到注册中心去
@EnableDiscoveryClient
@SpringBootApplication
@EnableBinding(MessageService.class)
//开启分布式事务
@EnableDistributedTransaction
@EnableTransactionManagement
public class ProviderRun {
public static void main(String[] args) {
SpringApplication.run(ProviderRun.class, args);
}
}
服务代码
@Service
public class SysServiceImpl implements SysService {
@Autowired
private SysUserMapper sysUserMapper;
/**
* @LcnTransaction//分布式事务
* @Transactional //本地事务
* @param sysUser
* @return
*/
@Override
@LcnTransaction(propagation = DTXPropagation.SUPPORTS)
@Transactional
public int add(SysUser sysUser) {
String groupId = TracingContext.tracing().groupId();
String applicationId = Transactions.getApplicationId();
System.out.println(groupId);
System.out.println(applicationId);
return sysUserMapper.add(sysUser);
}
}
关于DTXPropagation源码里面有解释
/**
* 当前没有分布式事务,就创建。当前有分布式事务,就加入
*/
REQUIRED,
/**
* 当前没有分布式事务,非分布式事务运行。当前有分布式事务,就加入
*/
SUPPORTS;
所以一般事物发起方使用REQUIRED,事物参与方使用SUPPORTS
这里因为 我是在消费者项目中调用的提供者的服务 ,这里的是 事物参与方 使用SUPPORTS
配置文件
server:
port: 8081
spring:
profiles:
include: dev
application:
name: provider1
eureka:
client:
serviceUrl:
defaultZone: http://eurka-server2-8060:8060/eureka/,http://eurka-server1-8080:8080/eureka/
instance:
#eureka服务器在接收到最后一个心跳之后等待的时间,然后才能从列表中删除此实例 默认90s(开发环境)
lease-expiration-duration-in-seconds: 10
#eureka客户端需要向eureka服务器发送心跳的频率 默认30s (开发环境)
lease-renewal-interval-in-seconds: 1
instance-id: ${spring.cloud.client.ip-address}:${server.port}
preferIpAddress: true
tx-lcn:
client:
manager-address: 127.0.0.1:8070
springcloud:
loadbalance:
enabled: true
微服务集群且用到 LCN事务模式时,为保证性能请开启TX-LCN重写的负载策略
tx-lcn.springcloud.loadbalance.enabled=true
消费者的pom
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
启动类
import com.codingapi.txlcn.tc.config.EnableDistributedTransaction;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Created by IntelliJ IDEA.
* 注解@SpringCloudApplication包括:@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker,分别是SpringBoot注解、注册服务中心Eureka注解、断路器注解
* @Author : WangCong
* @create 2020/5/9 9:47
*/
@SpringCloudApplication
@EnableFeignClients
//开启分布式事务
@EnableDistributedTransaction
@EnableTransactionManagement
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class,args);
}
}
配置文件
server:
port: 8084
spring:
profiles:
include: dev
application:
name: feign1
eureka:
client:
serviceUrl:
defaultZone: http://eurka-server2-8060:8060/eureka/,http://eurka-server1-8080:8080/eureka/
#healthcheck:
enabled: true #开启自定义健康检查
instance:
#eureka服务器在接收到最后一个心跳之后等待的时间,然后才能从列表中删除此实例 默认90s(开发环境)
lease-expiration-duration-in-seconds: 10
#eureka客户端需要向eureka服务器发送心跳的频率 默认30s (开发环境)
lease-renewal-interval-in-seconds: 1
instance-id: ${spring.cloud.client.ip-address}:${server.port}
preferIpAddress: true
#开启hystrix 熔断
feign:
hystrix:
enabled: true
tx-lcn:
client:
manager-address: 127.0.0.1:8070
springcloud:
loadbalance:
enabled: true
调用提供者代码
@RequestMapping("/add")
@Transactional
@LcnTransaction
public String test2(){
String groupId = TracingContext.tracing().groupId();
String applicationId = Transactions.getApplicationId();
System.out.println(groupId);
System.out.println(applicationId);
SysUser user = new SysUser();
user.setCreateTime(new Date());
user.setPassword("123");
user.setUsername("asdasd");
user.setSalt("asas");
user.setStatus(0);
helloService.list(user);
user.setUsername("xixixi");
List list=new ArrayList();
System.out.println(list.get(23));
int add = sysUserMapper.add(user);
return add+"";
}
这里我使用的是feign 的远程调用方式
/**
* Created by IntelliJ IDEA.
*
* @Author : WangCong
* @create 2020/5/9 9:58
*/
/**
* 远程服务名称 可以大小写
*/
@FeignClient(value = "provider1",fallbackFactory = TestFeignFallbackFactory.class)
public interface HelloService {
@RequestMapping("/service/hello")
public String hello();
@RequestMapping("/service/hello1")
public String hello1();
@RequestMapping("/list")
public String list(@RequestBody SysUser user);
}
所以在熔断的配置注意
熔断配置
import com.codingapi.txlcn.tc.support.DTXUserControls;
import com.codingapi.txlcn.tracing.TracingContext;
import com.wc.springcloud.model.SysUser;
import com.wc.springcloud.service.HelloService;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* Created by IntelliJ IDEA.
*
* @Author : WangCong
* @create 2020/5/9 10:26
*/
@Component
public class TestFeignFallbackFactory implements FallbackFactory<HelloService> {
@Override
public HelloService create(Throwable throwable) {
return new HelloService() {
@Override
public String hello() {
return throwable.getMessage();
}
@Override
public String hello1() {
return throwable.getMessage();
}
@Override
public String list(SysUser user) {
//回滚事务
DTXUserControls.rollbackGroup(TracingContext.tracing().groupId());
return throwable.getMessage();
}
};
}
}
注意这句话DTXUserControls.rollbackGroup(TracingContext.tracing().groupId());
如果没有配置,出现异常 事务不会回滚。
运行测试
提供者代码异常
查看数据库 事务回滚
注意
我之前代码 提供者 和消费者
提供者打印输出的是
消费者打印输出的是
第一个是组id,第二个是 每个服务的应用标识
两边的组id 都是一样的。
如果不一样,你配置的有问题