LCN实现分布式事务

LCN分布式事务实现

1、建库建表

1.1、库1:tx-manager

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(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

1.2、库2:测试业务数据txlcn-demo

create table `t_demo` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `kid` varchar(45) DEFAULT NULL,
  `group_id` varchar(64) DEFAULT NULL,
  `demo_field` varchar(255) DEFAULT NULL,
  `app_name` varchar(128) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

2、项目搭建

2.1、源码Demo

https://github.com/codingapi/txlcn-demo

2.2、项目结构

2.2.1、说明:事务的注解

  • LCN事务模式
    • 该模式对代码的嵌入性为低。
    • 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
    • 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
    • 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。
  • TCC事务模式
    • 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
      • Try: 尝试执行业务
      • Cancel: 取消执行业务,单独定义一个方法,事务取消时执行
      • Confirm:确认执行业务,单独定义一个方法,事务确认时执行
    • 该模式对有无本地事务控制都可以支持使用面广。
    • 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。
  • TXC事务模式
    • 该模式同样对代码的嵌入性低。
    • 该模式仅限于对支持SQL方式的模块支持。
    • 该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
    • 该模式不会占用数据库的连接资源。

2.3、代码实现

2.3.1、TM端

2.3.1.1、POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>txlcn-demo</artifactId>
        <groupId>org.txlcn.demo</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>txlcn-demo-tm</artifactId>
    <name>txlcn-demo-tm</name>
    <dependencies>
		<dependency>
		    <groupId>com.codingapi.txlcn</groupId>
		    <artifactId>txlcn-tm</artifactId>
		</dependency>
    </dependencies>
</project>
2.3.1.2、application.properties
spring.application.name=TransactionManager
server.port=7970
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=mysqladmin
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
2.3.1.3、主启动类
package org.txlcn.tm;
import com.codingapi.txlcn.tm.config.EnableTransactionManagerServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableTransactionManagerServer
public class TransactionManagerApplication {
    public static void main(String[] args) {
        SpringApplication.run(TransactionManagerApplication.class, args);
    }
}

2.3.2、TC端

2.3.2.1、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>
2.3.2.2、application.properties
# 默认之配置为TM的本机默认端口
tx-lcn.client.manager-address=127.0.0.1:8070 
2.3.2.3、主启动类
@SpringBootApplication
@EnableDistributedTransaction
public class DemoAApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoDubboClientApplication.class, args);
    }

}
2.3.2.4、接口
public interface DemoApiService {
    String execute(String name, String exFlag);
}
2.3.2.5、接口实现类
package org.txlcn.demo.dubbo.servicea;
import com.alibaba.dubbo.config.annotation.Reference;
import com.codingapi.txlcn.common.util.Transactions;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.codingapi.txlcn.tracing.TracingContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.txlcn.demo.common.db.domain.Demo;
import org.txlcn.demo.common.dubbo.DemoServiceB;
import org.txlcn.demo.common.dubbo.DemoServiceC;
import java.util.Date;
import java.util.Objects;
@Service
public class DemoApiServiceImpl implements DemoApiService {
    @Reference(version = "${demo.service.version}",
            application = "${dubbo.application.b}",
            registry = "${dubbo.registry.address}",
            retries = -1,
            check = false,
            loadbalance = "txlcn_random")
    private DemoServiceB demoServiceB;
    @Reference(version = "${demo.service.version}",
            application = "${dubbo.application.c}",
            retries = -1,
            check = false,
            registry = "${dubbo.registry.address}",
            loadbalance = "txlcn_random")
    private DemoServiceC demoServiceC;
    @Autowired
    private DemoMapper demoMapper;
    @Override
    @LcnTransaction
    public String execute(String name, String exFlag) {
        String bResp = demoServiceB.rpc(name);
        String cResp = demoServiceC.rpc(name);
        Demo demo = new Demo();
        demo.setGroupId(TracingContext.tracing().groupId());
        demo.setDemoField(name);
        demo.setAppName(Transactions.getApplicationId());
        demo.setCreateTime(new Date());
        demoMapper.save(demo);
        int a = 10/0;
        if (Objects.nonNull(exFlag)) {
            throw new IllegalStateException("by exFlag");
        }
        return bResp + " > " + cResp + " > " + "ok-service-a";
    }
}
2.3.2.6、Controller
package org.txlcn.demo.dubbo.servicea;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoConsumerController {
    @Autowired
    private DemoApiService demoApiService;
    @RequestMapping("/txlcn")
    public String sayHello(@RequestParam("value") String value,
                           @RequestParam(value = "ex", required = false) String exFlag) {
        return demoApiService.execute(value, exFlag);
    }
}

2.3.3、测试

访问:http://localhost:12004/txlcn?value=1

因为有int a = 10/0;必然报错,此时数据库没有数据就对了

去掉这个异常,可以插入,测试通过

猜你喜欢

转载自www.cnblogs.com/yjiu/p/12726165.html