分布式事务框架lcn入门demo

简介

LCN分布式事务框架其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果。

LCN5.0.2有3种模式,分别是LCN模式,TCC模式,TXC模式

LCN模式:

LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。
该模式的特点:

- 该模式对代码的嵌入性为低。
- 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
- 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
- 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。
 

TCC模式:

TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。

该模式的特点:

- 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
- 该模式对有无本地事务控制都可以支持使用面广。
- 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。
 

TXC模式:
TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL快走信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖redis分布式锁控制。
该模式的特点:

- 该模式同样对代码的嵌入性低。
- 该模式仅限于对支持SQL方式的模块支持。
- 该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
- 该模式不会占用数据库的连接资源。

实现原理

在这里插入图片描述
1.LCN客户端(发起方和参与方都必须要注册到事务协调者中), 建立一个长连接。(长连接 减宽带 但是消耗内存 连接后不断开)

2.订单服务(发起方)调用库存服务接口(参与方)之前,会向TxManager事务协调者创建一个事务的分组id。
3.订单服务(发起方)调用库存服务接口(参与方)的时候,会在请求头(底层是HTTP协议,LCN底层重写了Feigin客户端)中存放该事务的分组id,给库存服务。

(TxClient的代理连接池实现了Javax.sql.DataSource接口,并重写了close方法,事务模块在提交关闭后,TxClient连接池将执行‘假关闭’操作,等待TxManager协调完成事务后再关闭连接
4.如果库存服务获取到请求头中有对应的事务分组id,库存服务业务逻辑代码执行完毕的,会采用假关闭,不会提交该事务。

(微服务里面的服务和服务之间 协调在一起 通过注册中心。
分布式是事务中, 解决事务和事务之间的关系 靠的是类似的平台 TxManager 去协调事务
)

5.参与方在什么时候提交事务,不能一直不提交。
肯定在发起方 执行成功下。
订单服务(发起方)调用库存服务接口(参与方)之后,如果订单服务(发起方)执行没有问题的下,
订单服务(发起方)使用对应的事务分组id,通知给TxManager事务协调者,让后TxManager事务协调者在根据该事务分组id,通知给所有的参与方提交事务。

PS:长连接 好处减少宽带传输 弊端比较占内存。

使用LCN很简单 加个注解就OK了

入门demo

废话不多说,开整

SQL建表语句(在txlcn-tm模块的resource目录下)

/*
 Navicat Premium Data Transfer

 Source Server         : local
 Source Server Type    : MySQL
 Source Server Version : 100309
 Source Host           : localhost:3306
 Source Schema         : tx-manager

 Target Server Type    : MySQL
 Target Server Version : 100309
 File Encoding         : 65001

 Date: 29/12/2018 18:35:59
*/
CREATE DATABASE IF NOT EXISTS  `tx-manager` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
USE `tx-manager`;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_tx_exception
-- ----------------------------
DROP TABLE IF EXISTS `t_tx_exception`;
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,
  `ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 待处理 1已处理',
  `remark` varchar(10240) NULL DEFAULT NULL COMMENT '备注',
  `create_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 967 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

下载源码并编译
源码下载地址:https://github.com/codingapi/tx-lcn
工程目录
在这里插入图片描述
修改txlcn-tm的配置文件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
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update

# TM后台登陆密码,默认值为codingapi
tx-lcn.manager.admin-key=123456

注意:个人修改了数据库的名称,和用户名密码,TM的后台登录密码,其他根据自己的实际情况修改

txlcn-tm的pom修改,放开配置

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.codingapi.txlcn.tm.TMApplication</mainClass>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                        </manifest>
                        <manifestEntries>
                            <Class-Path>./</Class-Path>
                        </manifestEntries>
                    </archive>
                    <excludes>
                        <exclude>config/**</exclude>
                    </excludes>
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <descriptors>
                        <descriptor>src/main/build/package.xml</descriptor>
                    </descriptors>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

注掉配置

<!--docker 构建-->
            <!--<plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>1.0.0</version>
                <configuration>
                    <imageName>codingapi/txlcn-tm</imageName>
                    <dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                    <imageTags>
                        <imageTag>5.0.2</imageTag>
                    </imageTags>

                    <serverId>docker-hub</serverId>
                    <registryUrl>https://index.docker.io/v1/</registryUrl>
                </configuration>
            </plugin>
-->

最后使用maven编译,去掉test,否则编译很慢

启动txlcn-tm模块

启动后打开后台地址http://localhost:7970,初始密码是codingapi,我这里改成了123456
在这里插入图片描述

项目配置

本文以下订单扣库存举例。分为2个服务,并注册到eureka上

sql文件

test1库goods表,test2库order_form表

/*
 Navicat MySQL Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50712
 Source Host           : localhost:3306
 Source Schema         : test1

 Target Server Type    : MySQL
 Target Server Version : 50712
 File Encoding         : 65001

 Date: 09/01/2020 14:52:01
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for goods
-- ----------------------------
DROP TABLE IF EXISTS `goods`;
CREATE TABLE `goods`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `goods_id` int(10) NULL DEFAULT NULL,
  `stock` int(10) NULL DEFAULT NULL,
  `group_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of goods
-- ----------------------------
INSERT INTO `goods` VALUES (1, 31, 100, '');

SET FOREIGN_KEY_CHECKS = 1;


/*
 Navicat MySQL Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50712
 Source Host           : localhost:3306
 Source Schema         : test2

 Target Server Type    : MySQL
 Target Server Version : 50712
 File Encoding         : 65001

 Date: 09/01/2020 13:53:07
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for order_form
-- ----------------------------
DROP TABLE IF EXISTS `order_form`;
CREATE TABLE `order_form`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `goods_id` int(20) NULL DEFAULT NULL,
  `goods_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
  `user_id` int(10) NULL DEFAULT NULL,
  `create_time` timestamp(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 46 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

eureka

eureka代码不上了,很简单,上下配置文件

application.yml

server:
  port: 6868
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://127.0.0.1:${server.port}/eureka/
  server:
    enable-self-preservation: false
spring:
  application:
    name: eureka

order服务

controller

package com.wangtao.controller;

import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.wangtao.dao.OrderDao;
import com.wangtao.enity.Order;
import com.wangtao.feign.OrderFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@RestController
public class OrderController {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private OrderFeign orderFeign;

    @Transactional(rollbackFor = Exception.class)
    @LcnTransaction
    @RequestMapping("/createOrder")
    public String createOrder (){
        int i = 0;
        try {
            Order order = new Order();
            order.setGoodsName("图片");
            order.setGoodsId(31);
            order.setUserId(1);
            order.setCreateTime(new Date());
            orderDao.save(order);
            i = orderFeign.updateGoodsStock();
            int j=1/0;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("下单失败",e);
        }
        return "下单成功";
    }

}

dao

package com.wangtao.dao;


import com.wangtao.enity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface  OrderDao extends JpaRepository<Order, Integer>, JpaSpecificationExecutor<Order> {



}

enity

package com.wangtao.enity;

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

@Data
@Entity
@Table(name = "order_form")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增类型的主键
    private int id;

    @Column(name = "goods_id")
    private int goodsId;

    @Column(name = "goods_name")
    private String goodsName;

    @Column(name="user_id")
    private int userId;

    @Column(name = "create_time")
    private Date createTime;


}

feign

package com.wangtao.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "goods")
@Component
public interface OrderFeign {


    @RequestMapping(value = "/goods/updateGoodsStock", method = RequestMethod.POST)
    int updateGoodsStock();

}

启动类

package com.wangtao;

import com.codingapi.txlcn.tc.config.EnableDistributedTransaction;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableDistributedTransaction
public class OrderApplication {

	public static void main(String[] args) {
		SpringApplication.run(OrderApplication.class, args);
	}

}

配置文件

server:
  port: 9001
spring:
  application:
    name: order #指定服务名
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test2?characterEncoding=UTF8&useSSL=false
    username: root
    password: 123456
  jpa:
    database: MySQL
    show-sql: true
  cloud:
      refresh:
        refreshable: none
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    prefer-ip-address: true
tx-lcn:
    client:
        manager-address: 127.0.0.1:8070
    logger:
        enabled: true

pom.xml

<?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>parent</artifactId>
        <groupId>com.wangtao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>order</artifactId>


    <dependencies>
        <!-- 添加Eureka的依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.2.4</version>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--<dependency>-->
        <!--<groupId>org.springframework.cloud</groupId>-->
        <!--<artifactId>spring-cloud-starter-config</artifactId>-->
        <!--</dependency>-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>

        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.4</version>
            <classifier>jdk15</classifier>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.0.2</version>
        </dependency>



        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </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>

goods服务

controller

package com.wangtao.controller;

import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.codingapi.txlcn.tc.annotation.TxcTransaction;
import com.wangtao.dao.GoodsDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;



@RestController
@RequestMapping("/goods")
public class GoodsController {

    @Autowired
    private GoodsDao goodsDao;

    @RequestMapping("/updateGoodsStock")
    @Transactional(rollbackFor = Exception.class)
    @LcnTransaction //分布式事务注解
    public int updateGoodsStock(){
        int i = goodsDao.updateGoodsStock();
        return 1;
    }

    @RequestMapping("/updateGoodsStockTcc")
    @Transactional(rollbackFor = Exception.class)
    //@TccTransaction
    @TxcTransaction
    public int updateGoodsStockTcc(){
        int i = goodsDao.updateGoodsStock();
        return 1;
    }
}

dao

package com.wangtao.dao;


import com.wangtao.entity.Goods;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository

public interface  GoodsDao extends JpaRepository<Goods, Integer>, JpaSpecificationExecutor<Goods> {

    @Modifying
    @Query(value = "update goods set stock=stock-1 where goods_id=31 and stock>0",nativeQuery = true)
    public int updateGoodsStock();

}

entity

package com.wangtao.entity;

import lombok.Data;

import javax.persistence.*;

@Entity
@Data
@Table(name = "goods")
public class Goods {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增类型的主键
    private int id;

    @Column(name = "goods_id")
    private int goodsId;

    private int stock;


}

启动类

package com.wangtao;

import com.codingapi.txlcn.tc.config.EnableDistributedTransaction;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
@EnableDistributedTransaction
public class GoodsApplication {

	public static void main(String[] args) {
		SpringApplication.run(GoodsApplication.class, args);
	}

}

配置文件

server:
  port: 9002
spring:
  application:
    name: goods #指定服务名
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test1?characterEncoding=UTF8&useSSL=false
    username: root
    password: 123456
  jpa:
    database: MySQL
    show-sql: true
  cloud:
    refresh:
      refreshable: none
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    prefer-ip-address: true
tx-lcn:
    client:
        manager-address: 127.0.0.1:8070
    logger:
        enabled: true


pom.xml

<?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>parent</artifactId>
        <groupId>com.wangtao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>goods</artifactId>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- 添加Eureka的依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--<dependency>-->
        <!--<groupId>org.springframework.cloud</groupId>-->
        <!--<artifactId>spring-cloud-starter-config</artifactId>-->
        <!--</dependency>-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>


        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.0.2</version>
        </dependency>


        <!-- 热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <!-- <version>2.0.4.RELEASE</version>-->
            <!-- 启用 -->
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

分别启动两个服务并注册到eureka上,并访问http://localhost:9001/createOrder
发现基于lcn模式的数据库表中并没有增加数据,说明分布式事务起作用了

下面演示tcc模式

eureka配置不变

order服务

OrderController增加

    @Transactional(rollbackFor = Exception.class)
    @RequestMapping("/createTccOrder")
    @LcnTransaction
    public String createTccOrder (@RequestParam int num){
        int i = 0;
        try {
            Order order = new Order();
            order.setGoodsName("图片");
            order.setGoodsId(31);
            order.setUserId(1);
            order.setCreateTime(new Date());
            orderDao.save(order);
            i = orderFeign.updateGoodsStockTcc();
            int j=1/num;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("下单失败",e);
        }
        return "下单成功";
    }

OrderFeign增加

    @RequestMapping(value = "/goods/updateGoodsStockTcc", method = RequestMethod.POST)
    int updateGoodsStockTcc();

goods服务

GoodsController增加

 @RequestMapping("/updateGoodsStockTcc")
    @Transactional(rollbackFor = Exception.class)
    @TccTransaction(propagation = DTXPropagation.SUPPORTS,cancelMethod = "cancelUpdate",confirmMethod = "confirmUpdate" ,executeClass = GoodsController.class)
    public int updateGoodsStockTcc(){
        int i = goodsDao.updateGoodsStockTcc(TracingContext.tracing().groupId());
        return 1;
    }

    public void confirmUpdate() {
        log.info("成功");
    }

    @Transactional(rollbackFor = Exception.class)
    public void cancelUpdate() {
        log.error("失败,正在回退");
        goodsDao.cancelUpdate(TracingContext.tracing().groupId());
        log.error("回退成功");
    }

GoodsDao增加

    @Modifying
    @Query(value = "update goods set stock=stock-1,group_id=:groupId where goods_id=31 and stock>0",nativeQuery = true)
    public int updateGoodsStockTcc(@Param("groupId") String groupId);

    @Modifying
    @Query(value = "update goods set stock=stock+1 where group_id=:groupId",nativeQuery = true)
    void cancelUpdate(@Param("groupId") String groupId);

Goods增加

    @Column(name = "group_id")
    private String groupId;

启动2个服务注册到eureka上
输入网址正常情况下

在这里插入图片描述
order_form表
在这里插入图片描述
goods表
在这里插入图片描述
恢复原始数据,测试报错情况下
在这里插入图片描述
在这里插入图片描述
order_form表
在这里插入图片描述
goods表
在这里插入图片描述
ok,测试完毕,另外一种txc模式大家自行探索吧,溜了。。。

发布了12 篇原创文章 · 获赞 13 · 访问量 1648

猜你喜欢

转载自blog.csdn.net/qq_35818427/article/details/103906643