前言:写这个文章主要是给自己做下笔记以免遗忘,同时也给各大码农分享一下,互相学习。
分布式事务的需求来源于系统的服务化。在微服务系统中,无法使用传统的事务达到数据库的一致性。每个子服务都有自己独立的数据源,如果系统初期,没有分表分库,每个子服务的数据源都是连接同一个数据库主机,同一个数据库,那么还可以简单的利用传统的事务,增加代码的冗余达到事务效果。而往往如果已经需要考虑事务的时候,我们的系统应该已经达到了一定的用户量,开始了分表分库。故而分布式事务还是需要了解和使用的。
分布式事务有很多种实现方式,我在这主要谈论的是我熟悉的 “分三阶段提交模式”,这种模式的原理比较简单,当然了,性能上是有缺陷的。其他实现方式,可自行Google或者百度一下。

框架的特点

  1. 支持各种基于spring的db框架
  2. 兼容SpringCloud、Dubbo、motan
  3. 使用简单,低依赖,代码完全开源
  4. 基于切面的强一致性事务框架
  5. 高可用,模块可以依赖RPC模块做集群化,TxManager也可以做集群化
  6. 支持本地事务和分布式事务共存
  7. 支持事务补偿机制,增加事务补偿决策提醒
  8. 添加插件拓展机制

源码目录说明

  1. transaction-dubbo LCN dubbo rpc框架扩展支持
  2. transaction-springcloud LCN springcloud rpc框架扩展支持
  3. tx-client 是LCN核心tx模块端控制框架
  4. tx-manager 是LCN 分布式事务协调器
  5. tx-plugins-db 是LCN 对关系型数据库的插件支持
  6. tx-plugins-nodb 是LCN 对于无数据库模块的插件支持

不说太多了,直接上干货。(本文主要针对springcloud的)

1、去github.com下载tx-lcn-master源码
tx-lcn-master
目前更新到了4.1.0版本
LCN分布式事务框架的核心功能是对本地事务的协调控制,框架本身并不创建事务,只是对本地事务做协调控制。因此该框架与其他第三方的框架兼容性强,支持所有的关系型数据库事务,支持多数据源,支持与第三方数据库框架一块使用(例如 sharding-jdbc),在使用框架的时候只需要添加分布式事务的注解即可,对业务的侵入性低。LCN框架主要是为微服务框架提供分布式事务的支持,在微服务框架上做了进一步的事务机制优化,在一些负载场景上LCN事务机制要比本地事务机制的性能更好,4.0以后框架开方了插件机制可以让更多的第三方框架支持进来。
更多详情请参考官网 https://www.txlcn.org
我处理过的tx-lcn-master:https://pan.baidu.com/s/1M9SeulYe1j4ho9uuncWuBA
提取码:3ss3
2、配置分布式事务协调器TXmanager
把TXmanager配置好(redis连接信息,eureka注册中心,开放的端口),打包部署到自己的微服务中
主要修改的有三个地方:

#服务端口
server.port=8899

#eureka 地址
eureka.client.service-url.defaultZone=http://192.168.0.150:8761/eureka/
eureka.instance.prefer-ip-address=true

##redis 单点环境配置
#redis
#redis主机地址
spring.redis.host=127.0.0.1
#redis主机端口
spring.redis.port=6379
#redis链接密码
spring.redis.password=
spring.redis.pool.maxActive=10
spring.redis.pool.maxWait=-1
spring.redis.pool.maxIdle=5
spring.redis.pool.minIdle=0
spring.redis.timeout=0

eureka注册中心一定要配置自己系统的地址,关于TXmanager的运行机制在此不做太多的说明。
配置好了,打包发布即可进入被调用的状态。
3、服务中加入分布式事务管理(加入LCN事务管理)
1)把LCN框架源码编译安装到本地仓库

由于LCN的maven中央仓库不一定能下载到依赖,最好是自行把源码安装到本地仓库进行依赖。本人是这么做的。当然你也可以先尝试直接获取中央仓库的,如若不行再说。

2)pom.xml中添加LCN框架的依赖

     <properties>
        <!--lcn的版本-->
        <lcn.last.version>4.1.0</lcn.last.version>
    </properties>
    <!--LCN基于springcloud的分布式事务框架-->
    <dependency>
        <groupId>com.codingapi</groupId>
        <artifactId>transaction-springcloud</artifactId>
        <version>${lcn.last.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>*</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--LCN基于关系型数据模块的封装-->
    <dependency>
        <groupId>com.codingapi</groupId>
        <artifactId>tx-plugins-db</artifactId>
        <version>${lcn.last.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>*</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

transaction-springcloud LCN springcloud rpc框架扩展支持,我们这里使用的是springcloud
tx-plugins-db 是LCN 对关系型数据库的插件支持,我们这里使用的是mysql数据库

3)配置TXmanager的访问地址和端口号

spring.application.name = demo1
server.port = 8081
#${random.int[9000,9999]}
eureka.client.service-url.defaultZone=http://192.168.0.150:8761/eureka/

#txmanager地址
tm.manager.url=http://127.0.0.1:8899/tx/manager/

这里配置的txmanager地址就是我们在上面第二步配置的配置分布式事务协调器TXmanager并且部署到微服务环境中的子服务地址。我们发现,它还是使用传统的ip+port去访问的。虽然txmanager也作为子服务注册到了eureka上,但目前我还不确定是否可以根据applicationName访问到,读者可以试试。
4)分布式事务发起方
需要实现TxManagerTxUrlService和TxManagerHttpRequestService这两个接口,并作为bean注入到spring中。处理http请求和对服务器的连接

实现 TxManagerTxUrlService

import com.codingapi.tx.config.service.TxManagerTxUrlService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * create by lorne on 2018/11/18
 */
@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{

    @Value("${tm.manager.url}") //分布式事务协调器TXmanager访问地址
    private String url;

    @Override
    public String getTxUrl() {
        System.out.println("load tm.manager.url ");
        return url;
    }
}

实现 TxManagerHttpRequestService

    import com.codingapi.tx.netty.service.TxManagerHttpRequestService;
    import com.lorne.core.framework.utils.http.HttpUtils;
    import org.springframework.stereotype.Service;
    /**
     * create by lorne on 2018/11/18
     */
    @Service
    public class TxManagerHttpRequestServiceImpl implements TxManagerHttpRequestService{

    @Override
    public String httpGet(String url) {
        System.out.println("httpGet-start");
        String res = HttpUtils.get(url);
        System.out.println("httpGet-end");
        return res;
    }

    @Override
    public String httpPost(String url, String params) {
        System.out.println("httpPost-start");
        String res = HttpUtils.post(url,params);
        System.out.println("httpPost-end");
        return res;
    }
}

加入分布式事务注解@TxTransaction(isStart = true),开启分布式事务。isStart = true声明为分布式事务发起方

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private Demo2Feign demo2Feign;//远程调用,同一数据库,有事务
    @Autowired
    private Demo3Feign demo3Feign;//远程调用,不同数据库,有事务
    @Autowired
    private TestMapper testMapper;

    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public List<Test> list() {
        return testMapper.findAll();
    }

    @Override
    @TxTransaction(isStart = true)
    @Transactional
    public int save() {
        int rs1 = testMapper.save("mybatis-hello-1", new Date());
        logger.info("本地服务数据插入成功");
        int rs2 = demo2Feign.save();
        logger.info("远程服务2号数据插入成功");
        int rs3 = demo3Feign.save();
        logger.info("远程服务3号数据插入成功");
        logger.info("插入" + (rs1 + rs2 + rs3) + "条记录");
        logger.info("制造异常");
        int v = 100 / 0;
        return rs1 + rs2 + rs3;
    }
}

如上代码执行完成以后所有参与此分布式事务模块都将回滚事务。

5)分布式事务被调用方
需要实现TxManagerTxUrlService个接口,并作为bean注入到spring中。处理对服务器的连接

import com.codingapi.tx.config.service.TxManagerTxUrlService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * create by lorne on 2018/11/18
 */
@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{

    @Value("${tm.manager.url}") //分布式事务协调器TXmanager访问地址
    private String url;

    @Override
    public String getTxUrl() {
        System.out.println("load tm.manager.url ");
        return url;
    }
}

加入分布式事务注解@TxTransaction,或者实现ITxTransaction接口,开启分布式事务。等待起调方发起事务

@Service
public class DemoServiceImpl implements DemoService, ITxTransaction {

    @Autowired
    private TestMapper testMapper;

    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public List<Test> list() {
        return testMapper.findAll();
    }

    @Override
    @Transactional
    public int save() {
        int rs = testMapper.save("mybatis-hello-2", new Date());
        logger.info("插入" + rs + "条记录");
        return rs;
    }
}

6)说明:在使用LCN分布式事务时,只需要将事务的开始方法添加@TxTransaction(isStart=true)注解即可,在参与方添加@TxTransaction或者实现ITxTransaction接口即可。详细查看官网说明
7)demo
https://pan.baidu.com/s/1IaTilRxmFQBNj9H0W6BFzg
提取码:979b

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐