【第2章】Seata1.3.0源码编译+参数配置+启动运行(超详细)

一、序言

      本来想用一天时间更完的,后来写完才发现,我去,想的太简单了,草率了草率了;由于强迫症的原因,再加上平时要搞其他的,结果就导致我花了近3天的碎片时间梳理、配图、配文去完成这篇博文;

     本来后续还有讲nacos和sentinel的,还有围绕seata分布式事务中间件,结合电商商品下单业务场景搭建的微服务模块的详细说明;现在看来,一是时间不允许了(再更下去,估计要没完没了了),二是seata的这篇博文中我已经说了太多太多了,至于大家关心的微服务架构的项目案例是如何一步步搭建和使用的,我想已经没必要再细说了,所以,大家直接克隆GitHub上最终的项目源码到本地就行了,里面的模块和注释都很清晰,没有什么难点,就是CRUD操作+Seata和Nacos配置。

     下面将相关的项目地址、安装的服务、软件环境及seata使用过程中容易出现的两个问题放出来,仅供各位参考:


GitHub项目地址:

seata-spring-boot-demos

Gitee码云项目地址

seata-spring-boot-demos

附带两个文档,文档中有一些内容可能说的不对,懒得更新了(有时间再说,有问题留言吧)


附带sentinel限流的简单应用

定义限流规则并加载


使用限流规则


调用接口,测试限流规则是否生效

正常一秒刷新一次接口


正常一秒刷新两次+接口


还有一种方式是通过sentinel控制台来使用的,可以参考官网文档进行quick-start


我的Seata(fork)项目地址:

GitHub - kobeyk/seata at 1.3.0

文中涉及到的修改均已推上去了



Nacos(1.3.2)服务包下载地址:

链接:https://pan.baidu.com/s/1V8B_xBL25HzUJY6-yHljJg 
提取码:pcr9 


MySql8(win64)软件安装包下载地址:

链接:百度网盘 请输入提取码 
提取码:itle 


链接:https://pan.baidu.com/s/1-yyx7hz_a-9jPoqZySy0hg 
提取码:lb9n 




Seata AT模式注意事项:


切记不要再RM业务(service)层中的方法上加@Transactional注解:

二、什么是Seata?

官网地址:https://seata.io


提炼关键词:

特性:

1、一致性(这个是痛点,解决的就是微服务分布式事务一致性的问题)

2、高性能(这个是卖点,如果用了反而拖累整个微服务架构的性能,试问谁还会用呢?)

3、易用性(这个是亮点,大家开发时引入新技术,都希望简单好用,如果技术太过于复杂,反而会提升使用门槛,吓跑用户)


事务模式

  • AT (Automatic Transaction,对业务系统无侵入,省去了用的人很多编码的工作)
  • TCC(Try-Confirm-Cancel,对业务系统有侵入,用的时候需要自己结合业务系统写大量的代码)
  • Saga (Long Live Transaction,长事务解决方案,对业务系统有侵入,同TCC)
  • XA ( X/Open 组织提出的分布式事务处理的规范,对业务系统无侵入,只需要DB实现了XA协议即可)


  

文章推荐:带你读透 SEATA 的 AT 模式

这篇文章讲的很好,很适合结合着Seata官方文档细细品读,下面放几张文中的关键图

Seata-AT模式最初的构想

  • 每个应用服务对应一个本地数据库,即database per service;
  • 每个应用服务之间互相协作,调用接口;
  • 每个应用服务执行自己的SQL脚本;
  • 每个应用服务执行自己的SQL语句时,是基于本地事务的;
  • 每个应用服务对应的本地库中,都存在一个UNDO Log(回滚日志记录);
  • 每个应用服务实现自己的事务提交,如果有一个事务提交失败,则全局回滚;
  • 发生全局事务回滚时,则每个分支(本地)事务按照UNDO log的内容对数据集进行反向SQL补偿


Seata分布式事务中间件架构图

搞懂上图,需要知道Seata的三个角色概念:

  • TC(Trasaction Coordinator,事务协调器)
  • TM(Transaction Manager,事务管理器)
  • RM (Resource Manager,资源管理器)

其中TC对应的应用服务就是seata-server

TC角色主要用来和TM和RM进行“交流”的,通过RPC(Netty)+服务注册发现(Nacos等),实现与各个微服务模块之间的通信



其中TM对应的应用服务就是开启了@GlobalTransactional注解的业务方法所在的微服务模块,即seata-business-server

TM角色主要定义了全局事务的边界,用来向TC开启一个全局事务,拿到全局事务唯一的XID,并在调用的微服务之间的上下文中进行传递,同时,该服务还掌握着是否全局提交还是全局回滚的决议权!

TM#开启全局事务1#TransactionalTemplate.beginTransaction(...)

TM#开启全局事务2#DefaultGlobalTransaction.begin(int timeout, String name)

TM#开启全局事务3#DefaultTransactionManager .begin(.....)

TM#开启全局事务4#AbstractNettyRemoting.sendSync(.....)

TM客户端调用过程结束,接下我们来看下TC服务端的


TC#开启全局事务1#DefaultCoordinator.doGlobalBegin(.....)

TC#开启全局事务2#DefaultCore.begin(.....)

TC#开启全局事务3#GlobalSession.begin()

TC#开启全局事务4#DataBaseSessionManager.addGlobalSession(GlobalSession session)

TC#开启全局事务4#DataBaseTransactionStoreManager .writeSession (....)

TC#开启全局事务5#LogStoreDataBaseDAO .insertGlobalTransactionDO (....)

executeUpdate()方法执行完后,我们来看下TC服务对应的全局表中的记录


全局提交的条件是:各个微服务的分支事务均提交成功;

全局回滚的条件是:只要有一个微服务的分支事务提交失败,就会触发TM发起整个全局事务的回滚!然后由TC基于XID分别异步调用各个RM,并对RM的数据源进行代理,通过RM数据源的代理,一一基于各个RM的undo_log表进行反向SQL的补偿,以达到整个微服务架构的数据结果的一致性;



RM角色对应的应用服务就是在业务上存在SQL语句执行的微服务模块,如order-server

RM角色主要用来向TC注册分支事务(保存到seata库对应的branch_table表中),并上报分支事务提交和回滚的状态给TC,TC根据分支事务提交和回滚的状态来驱动TM最终进行全局的提交或全局的回滚;

(关于RM和TC交互的代码调试就不放了,下面放几张和分支事务有关的截图吧,有问题的可以留言)

库存服务(RM)对应的初始化商品的库存记录如下:

TC分支事务表记录如下:

库存服务(RM)在TM开启全局事务后,执行减库存操作但事务未提交时的分支事务记录如下:

库存服务(RM)在TM开启全局事务后,执行减库存操作,且在事务未提交时的回滚日志记录如下:

每个分支事务都对应一条undo_log记录,我们导出减库存XID对应的的log后,查看其内容如下:


最后执行成功后,相应的undo_log记录会被删除,刷新库存的undo_log表记录(空)如下:

最终,我们可以看到,水杯的库存量为:


如果TM发起全局提交,则TC会分别调用各个微服务,并对各个RM中的undo_log表中的XID记录进行异步删除;

如果TM发起全局回滚,则TC会分别调用各个微服务,并对各个RM中的undo_log表中的XID对应的记录进行反向SQL补偿,以恢复事务提交失败前的数据;



三、Seata源码编译

Seata项目开源地址:GitHub - seata/seata: Seata is an easy-to-use, high-performance, open source distributed transaction solution.

登录GitHub,将其fork到自己的仓库中


有两种方式可以对seata1.3.0源码进行编译:

  1. Clone到本地,导入到idea中进行编译

  2. 下载源码包,解压到本地,导入到idea中进行编译


最新seata1.3.0封版时间在:2020年7月16号(离本篇博客写的时候,时间也就差了3个月)



第一种方式:

这种方式拉代码有些慢,稍作等待即可



clone完成后,记得切换下分支(修改的代码后面提交都是基于这个最新分支的):


(1)使用【Module from Existing Sources】构建Maven项目


(2)使用下面的方式,逐一添加pom进行Maven构建




大致简单地来说一下seata各个模块的功能:


由于目前只导入了all模块中的pom(seata项目中所有模块依赖的jar)和父pom,其他子pom还未导入,因此,接下来就是一个个点,将各个模块的pom添加进来了,如将config配置模块中涉及的所有pom文件进行导入如下:

其他模块导入不在一一说明,最后全部导入完,Maven也完成相应的jar包下载后,且IDEA编译无误后,即可使用seata服务了


插曲:这里构建编译项目的时候,会报下面这个错误

Windows下配置protobuf的文章可以参考我之前的博文:

Google Protocol Buffer -- Windows下Python的应用

不想编译转换proto文件到java类的,直接注释掉这个Grpc测试类再次编译即可


第二种方式(同样,也是要注意切换分支的):



压缩包2.3M可以看出整个seata项目是很小的


解压后的目录结构和导入IDEA中编译的方式同第一种一样,这里就不再重复说了!


四、Seata服务参数配置

        上一步源码的编译工作完成后,接下来我们看下关于Seata的相关配置

(1)Seata库对应的数据库脚本文件(以mysql为例)



二话不说,先在mysql中建一个seata库,然后将上述的脚本执行后,效果如下:


(2)Seata分布式事务解决方案,AT事务模式下,客户端相关的SQL脚本(以mysql为例)

这个在构建微服务模块的时候,把该脚本执行在各个RM对应的数据库中即可


(3)Seata服务参数配置

这里直接贴出来,修改后的config.txt如下(只放修改的参数key和value,没修改的不放):

service.vgroupMapping.seata_example_tx_group=default
store.mode=db
store.db.datasource=hikari
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&autoReconnect=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC
store.db.user=root
store.db.password=root123

注意四个地方:

  • seata_example_tx_group要和客户端的配置保持一致

  • 存储模式由原来的file-->db

  • 只有当存储模式改为db后,数据库连接池配置才起作用,将原来的druid-->hikari

  • 使用mysql8+库,注意驱动包名一定要改过来,将原来com.mysql.jdbc.Driver-->com.mysql.cj.jdbc.Driver


一旦将mysql驱动包升级到8后(由于seata1.3.0中使用的是mysql5.+)


启动seata服务时,会报如下的错误(前提是上述seata参数信息被成功推送到了配置中心,如nacos;后面再说到seata启动的时候,会讲一下,这种情况是如何解决的):

Exception in thread "main" io.seata.common.loader.EnhancedServiceNotFoundException: 
not found service provider for : io.seata.server.session.SessionManager caused by 
java.lang.IllegalStateException: Extension instance(definition: 
io.seata.common.loader.ExtensionDefinition@199a697f, class: interface 
io.seata.server.session.SessionManager)  could not be instantiated: not found service 
provider for : io.seata.core.store.db.DataSourceProvider caused by 
java.lang.IllegalStateException: Extension instance(definition: 
io.seata.common.loader.ExtensionDefinition@c5a49597, class: interface 
io.seata.core.store.db.DataSourceProvider)  could not be instantiated: Failed to load 
driver class com.mysql.cj.jdbc.Driver in either of HikariConfig class loader or Thread 
context classloader


(4)Seata服务(TC)配置服务注册+发现和配置中心

4.1 服务注册+发现中心,改成Nacos

关于Seata和Nacos的配置,在GitHub项目中也有相应的doc说明:

Nacos(1.3.2)服务包下载地址

百度网盘:百度网盘 请输入提取码

提取码:pcr9


4.2 配置中心,改成Nacos


(5)将Seata配置文件(config.txt)中的内容推送到Nacos配置中心

定位sh脚本文件如下:

在当前目录空白处右键打开git bash终端,执行命令如下:

sh nacos-config.sh -h 127.0.0.1



总过79个数据配置项被成功的推到了nacos的配置中心,失败0个

我们看下nacos库中的配置表,查询如下:


至此,整个seata的相关配置就全部完成了,接下来,就是启动seata服务了


五、Seata服务启动

源码也编译了,配置也配完了,接下来,我们启动下seata服务:


我去,启动居然报错了???


仔细查看报错的信息,发下内容如下:

Caused by: java.lang.RuntimeException: Failed to load driver class com.mysql.cj.jdbc.Driver
 in either of HikariConfig class loader or Thread context classloader
	at com.zaxxer.hikari.HikariConfig.setDriverClassName(HikariConfig.java:486)


很明显,基于mysql8的最新驱动名称“com.mysql.cj.jdbc.Driver”无法被加载,前面seata参数配置的时候,说过,seata默认mysql连接驱动是5.+的,你现在给他mysql8的驱动包名,让他去找,当然是找不到的,怎么办,不要慌,我们把源码(server)中的mysql依赖包换成8不就行了吗,具体操作如下:

(1)首先,看下父pom中有没有关于mysql8的版本定义


(1)其次,我们修改server模块中的pom,将mysql驱动包的版本更换下


刷新之后,我们再看下


mysql驱动版本换过来后,我们再次启动server,看下是否成功(卖个关子):

居然又TM的失败了,莫慌,我们看下具体错误原因是什么:

Caused by: java.sql.SQLException: The server time zone value '�й���׼ʱ��' is 
unrecognized or represents more than one time zone. You must configure either the server or 
JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time 
zone value if you want to utilize time zone support.
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-
connector-java-8.0.19.jar:8.0.19]

错误原因很明显,必须配置driver的服务端时区(MySQL JDBC 6.0 版本以上必须配置此参数),这就很奇怪了,我不是在config.txt中指定了吗??? 纳尼!!!


而且往nacos配置中心推送也成功了,我曾一度怀疑config.txt没有保存或者nacos没有刷新到最新内容:

实操证明,两者都不是:


那究竟是因为什么呢?试过来试过去,最终发现"&"符号很可疑,而且&符号及其后面的内容都被截断了??? 解铃还须系铃人,我们看下nacos-config.sh脚本是怎么写的

很明显,这是一个curl命令,向nacos提供的config配置接口发送seata的数据配置项

话又说回来,在url传值中,有时候我们需要加很多参数,但是有的时候用url传递多个参数的时候&后面的参数被自动截取了,后面的参数显示不出来了,这不是我们所希望的

怎么办呢?好办,把&用asc值表示,即%26

再次修改store.db.url的内容如下

store.db.url=jdbc:mysql://127.0.0.1:3306/seata?
useUnicode=true%26autoReconnect=true%26characterEncoding=utf-
8%26useSSL=false%26allowMultiQueries=true%26serverTimezone=UTC

再次执行nacos-config.sh如下:


再次刷新store.db.url的内容如下:

可以看到,内容已经同步更新过来了


再次启动seata-server


刷新下Nacos的服务列表如下:


六、结语

        到此为止,整个seata1.3.0的源码从编译,到seata的配置,再到seata-server的启动就算全部讲完了,后面就是结合具体的微服务业务场景,结合seata,来一场分布式事务的实战测试了


TM客户端:


TC服务端

=====================完结!!!!!!!!!!!!!!!====================

  • 13
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
Seata的高可用模式是通过TC使用db模式共享全局事务会话信息,使用非file的seata支持的第三方注册中心和配置中心来共享全局配置的方式来实现的。 Seata支持的第三方注册中心有nacos 、eureka、redis、zk、consul、etcd3、sofa、custom,支持的第三方配置中心有nacos 、apollo、zk、consul、etcd3、custom。seata官方主推的应该是nacos(毕竟是一家的),但是由于本人平常使用的注册中心一直是zk,因此首先考虑通过zk作为注册中心来实现高可用模式。 环境准备 zk环境准备 本地已安装zk的可以忽略,如果本地未安装,先在本地安装zk,具体安装自行百度。 PS: 此处如果使用的是远程zk服务器,则本地可以只下载,不安装。 数据库环境准备 1、创建数据库seata 2、执行源码(version1.2.0)script -> server -> db 下面的mysql.sql文件,建立global_table,branch_table,lock_table表。 配置信息导入zk 1、打开本地seata源码(版本1.2.0) 2、编辑script -> config-center -> config.txt文件,修改store.mode=db,修改store.db相关的数据库连接信息,其它默认即可 3、进入script -> config-center ->zk,执行 sh zk-config.sh -h 127.0.0.1 -p 2181 -z /usr/local/zookeeper-3.4.14(-z 后面的参数为你本地zk目录) 上面命令会将config.txt中的配置信息写入zk的/seata节点下。 启动tc-server 1、编辑conf下面的registry.conf文件,修改registry.type=zk,修改config.type=zk,修改registry.zk及config.zk信息,如下: 注意:config的zk配置没有cluster属性。 2、启动server,在本地seata安装目录bin目录下执行./seata-server.sh -m db (此处也可以直接编译本地源码然后启动Server模块下的Server类)。 不出意外的话,启动会报错,错误信息是从zk读取配置的时候反序列化出问题。 错误原因:序列化问题,由于使用seata自带的zk-config.sh脚本向zk写入配置信息的时候,采用的序列化方式相当于String.getBytes(),而框架读取配置的时候使用的是org.101tec包中的Zkclient客户端,反序列化使用的是该包下面的SerializableSerializer序列化类,使用的ObjectOutputStream进行反序列化,和序列化方式不一致。 该问题在1.3.0版本中解决,解决方式是seata支持序列化方式配置,支持自定义序列化方式,同时提供默认序列化实现类DefaultZkSerializer,反序列化实现为new String()。 到此处,1.2.0版本无法进行下去,由于目前1.3.0正式版本还未出,只能拉取最新的开发分支源码,本地编译打包1.3.0-SNAPSHOT版本。 后续版本切为1.3.0-SNAPSHOT(20200701),删除原zk配置信息重新导入1.3版本的config.txt文件信息。 本地源码编译后直接Idea启动Server类。启动成功。 PS:启动日志里面会有一些getConfig失败的报错,这些不用管,这些新的配置参数1.3版本新增的,由于当前是SNAPSHOT版本,还不完善。 PS: 如果遇到getServerCharset 空指针异常,这个主要是MySQL和MySQL驱动版本不一致引起的,看https://blog.csdn.net/zcs20082015/article/details/107087589 服务启动 配置修改 简单处理,这里不再建新的模块,直接使用zhengcs-seata-storage模块作为演示。 1、修改POM,引入zkclient,修改seata版本 2、修改application.yml,将注册和配置类型改为zk 另外需要注意的是seata.tx-service-group配置参数要和zk导入的配置信息相关参数保持一致,否则会找不到server集群 启动服务 1、引入全局事务 2、启动 测试 基本功能测试 单元测试用例: 手动插入异常 执行用例: 基本功能是没问题的,更详细全面的测试这里就不介绍了,大家自行尝试。 高可用测试 上面的单机版肯定无法满足高可用,tc-server一旦宕掉,整个全局事务会无法进行回滚,同时会在seata库下面的事务表里面留下事务记录(正常处理成功后会被删除)。 seata的高可用是通过多个tc-server实例组成的集群来实现的。 启动多个tc-server实例: 通过-p参数修改启动接口,同时勾选Allow parallel run,开启多个实例。 然后启动客服端服务: 从启动日志可以看出,客户端会同时向所有几点注册TM和RM。 执行测试用例: 那,如果在数据已提交,异常退出之前把对应的tc-server节点停掉,会怎么样呢?答案是:全局事务回滚。大家自行尝试一下。 还有一种情况,如果客户端在执行过程中中断了,会怎么样? 如果客户端是单节点部署,那么: 首先,seata库下面的事务处理表里面有遗留事务处理记录,然后你会发现tc-server端日志里面会持续刷上述日志,tc-server会持续的尝试回滚该事务。最遗憾的是:哪怕客户端服务重启,也不会回滚该事务!!! 不过还好的是,这种情况毕竟是特例,如果客户端服务是集群部署,那么seata是可以正常完成事务回滚的。 结语 从上面的情况来看,起码seata对于简单的分布式事务场景的高可用支撑是没问题的,但是seata毕竟还是一个新框架,在实际的复杂的业务场景下会否出现什么问题,其实应该说出现什么问题都是可能的,这个需要实践和时间才能出真知了。 另外,seata目前没有提供控制台,没有服务集群配套的HA机制,这个不知道什么时候会出,拭目以待,seata应该会是一个很不错的分布式事务解决方案。   参考资料 https://seata.io/zh-cn/docs/ https://github.com/seata/seata ———————————————— 版权声明:本文为CSDN博主「zhengcs已被占用」的原创文,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/zcs20082015/article/details/107092936

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值