分布式事务TX-LCN之客户端搭建并测试

框架定位

LCN并不生产事务,LCN只是本地事务的协调工

TX-LCN定位于一款事务协调性框架,框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果。

解决方案

在一个分布式系统下存在多个模块协调来完成一次业务。那么就存在一次业务事务下可能横跨多种数据源节点的可能。TX-LCN将可以解决这样的问题。

例如存在服务模块A 、B、 C。A模块是mysql作为数据源的服务,B模块是基于redis作为数据源的服务,C模块是基于mongo作为数据源的服务。若需要解决他们的事务一致性就需要针对不同的节点采用不同的方案,并且统一协调完成分布式事务的处理。

upload successful

准备工作

jdk1.8、mysql、redis、idea

1、数据库脚本


CREATE DATABASE `txlcn-demo` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `txlcn-demo`;

CREATE TABLE IF NOT EXISTS `t_demo`(
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `kid` varchar(45) DEFAULT NULL,
  `demo_field` varchar(255) DEFAULT NULL,
  `group_id` varchar(64) DEFAULT NULL,
  `unit_id` varchar(32) 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、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>tx-lcn-demo</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>demo-service-a</module>
        <module>demo-service-b</module>
        <module>demo-service-c</module>
        <module>demo-service-common</module>
    </modules>

    <artifactId>txlcn-client-demo</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>
common依赖
<?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-client-demo</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>demo-service-common</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <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>
    </dependencies>
</project>
service依赖

serviceA,serviceB,serviceC的三个依赖是一样的,只有artifactId改变了

<?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-client-demo</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>demo-service-c</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>demo-service-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>
    </dependencies>
</project>

3、common代码

config
package com.demo.txlcn.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * 描述:
 *
 * @author 含光
 * @email jarvan_best@163.com
 * @date 2021/2/24 7:43 下午
 * @company 数海掌讯
 */
@Configuration
public class RestTemplateConfiguration {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
entity
package com.demo.txlcn.common.entity;

import com.codingapi.txlcn.common.util.id.RandomUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * 描述:
 *
 * @author 含光
 * @email jarvan_best@163.com
 * @date 2021/2/24 3:13 下午
 * @company 数海掌讯
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Demo {
    private Long id;
    private String kid = RandomUtils.randomKey();
    private String demoField;
    private String groupId;
    private Date createTime;
    private String appName;
}
mapper
package com.demo.txlcn.common.mapper;

import com.demo.txlcn.common.entity.Demo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

/**
 * 描述:
 *
 * @author 含光
 * @email jarvan_best@163.com
 * @date 2021/2/24 3:13 下午
 * @company 数海掌讯
 */
@Mapper
public interface BaseDemoMapper {

    @Insert("insert into t_demo(kid, demo_field, group_id, create_time,app_name) values(#{kid}, #{demoField}, #{groupId}, #{createTime},#{appName})")
    void save(Demo demo);
}

4、三个服务的applicationl.yml

复制一份更改下面三个参数即可

spring.application.name=demo-service-a

server.port12011

txlcn-demo库账号密码

日志库账号密码

server:
  port: 12011

spring:
  application:
    name: demo-service-a ##服务名称
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://【你的数据库地址】:3306/txlcn-demo?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
    username: 【你的数据库名称】
    password: 【你的数据库密码】
tx-lcn:
  ribbon:
    loadbalancer:
      dtx:
        # 是否启动LCN负载均衡策略(优化选项,开启与否,功能不受影响)
        enabled: true
  logger:
    # 开启日志,默认为false
    enabled: true
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://【你的数据库地址】:3306/tx_logger?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
    username: 【你的数据库名称】
    password: 【你的数据库密码】
  client:
    # 默认之配置为TM的本机默认端口
    manager-address: 127.0.0.1:8070
ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000

5、service代码

controller
package com.demo.txlcn.demo;

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;

/**
 * 描述:
 *
 * @author 含光
 * @email jarvan_best@163.com
 * @date 2021/2/24 3:26 下午
 * @company 数海掌讯
 */
@RestController
public class DemoController {

    private final DemoService demoService;

    @Autowired
    public DemoController(DemoService demoService) {
        this.demoService = demoService;
    }

    /**
     * 正常情况返回 ok-service-b > ok-service-c > ok-service-a
     * http://localhost:12011/txlcn?value=4561
     * @param value 保存值
     * @param exFlag 是否抛出异常
     * @param flag
     * @return
     */
    @RequestMapping("/txlcn")
    public String execute(@RequestParam("value") String value, @RequestParam(value = "ex", required = false) String exFlag
            , @RequestParam(value = "f", required = false) String flag) {
        return demoService.execute(value, exFlag, flag);
    }
}
mapper
@Mapper
public interface DemoMapper extends BaseDemoMapper {
}
demoService
public interface DemoService {
    String execute(String value, String ex, String exValue);
}
serviceImpl
package com.demo.txlcn.demo;

import com.codingapi.txlcn.common.util.Transactions;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.codingapi.txlcn.tracing.TracingContext;
import com.demo.txlcn.common.entity.Demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;

/**
 * 描述:
 *
 * @author 含光
 * @email jarvan_best@163.com
 * @date 2021/2/24 3:29 下午
 * @company 数海掌讯
 */
@Service
public class DemoServiceImpl implements DemoService {
    @Resource
    private DemoMapper demoMapper;
    @Autowired
    private RestTemplate template;


    @LcnTransaction
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String execute(String value, String exFlag, String flag) {
        String dResp = template.getForObject("http://localhost:12012/txlcn?value=" + value, String.class);
        String eResp = template.getForObject("http://localhost:12013/txlcn?value=" + value, String.class);

        Demo demo = new Demo();
        demo.setGroupId(TracingContext.tracing().groupId());
        demo.setDemoField(value);
        demo.setCreateTime(new Date());
        demo.setAppName(Transactions.getApplicationId());
        demoMapper.save(demo);

        if (Objects.nonNull(exFlag)) {
            throw new IllegalStateException("exception by exFlag");
        }

        return dResp + " > " + eResp + " > " + "ok-service-a";
    }
}

工程结构

img

6、启动类增加EnableDistributedTransaction注解并启动tm和三个测试服务

启动完成到tm后台查看,正常结果如下 img

7、测试

正常情况,事务正常提交

正常访问 http://localhost:12011/txlcn?value=测试分布式事务-lcn

事务全部提交,txlcn-demo表中增加三条数据

img

数据库txlcn-demo中增加如下三条数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值