seata分布式事务实践,图解步骤

分布式事务是业界一个老生常谈的问题了,也有很多种解决方案。本文并不会去很生硬的讲那些解决方案,也不会去对比啥啥啥,这里就是很纯粹的把分布式解决方案之一的seata搭建一套可运行的环境,供大家参考和学习。我始终认为,理论是基础,而实践跑demo是我们去了解原理的第一步。如果连最基本的demo都跑不起来,那么我相信去理解官网上的那些文档定会很枯燥,而且不一定能get到点,甚至还会怀疑TMD是不是文档写错了,我怎么按照文档来还是跑步起来,或者跑起来了并没有生效...心里顿时MMP。其实,只有一步步实践了,躺了坑,爬起来以后才会有兴趣详去了解底层原理。
 
 
为了大家能更清楚理解整个服务调用流程,及seata(client、server)跟nacos之间的交互关系,下面先给个图,站在全局的视角去理解,可能会更加明白下面的demo安装和启动流程。
 

 

 
 
本文使用的nacos、seata的版本都是1.4.0的,请各位读者注意版本,以免出现一些未知问题。
 
1、下载nacos配置中心的可运行包或者源码
 
关键配置:
 
Nacos 注册中心服务依赖的DB-SQL,如下:新建DB,然后初始化该脚本
 
上图给出nacos的源码截图,是让大家大概清楚改啥配置,初始化啥脚本。以及如果还是有疑问那么直接撸源码分析。
下面启动方式没有使用源码启动,其实也是可以直接在IDEA中使用源码启动,源码运行的main 方法入口在:nacos/console/Nacos类中,注意如果本地自测,请添加如下启动参数:-Dnacos.standalone=true
 
启动nacos 服务(这里就没有使用源码方式启动了,而是直接使用可运行的包,执行如下shell命令完成)
进入 bin 目录
sh startup.sh -m standalone  执行该命令.【提醒:如果使用单机模式启动,并且不配置application.properties中spring.datasource.platfrm,那么存储默认使用Derby数据库,而不是mysql。如需要mysql存储,那么就要打开其配置。  PS:如果使用集群模式启动,需要新增配置项:nacos/conf/cluster.conf】
查看日志,确认是否启动OK
tail -1000f  ../logs/start.out  
观察日志,未报错说明启动成功,如下图正常启动
 
启动成功后访问地址: http://127.0.0.1:8848/nacos/  输入:nacos/nacos 可进入
 
管理命名空间:这个对配置文件和服务列表能统一进行分类和管理。如下图:新建新的命名空间,下文会seata-server的配置初始化到该命名空间下,就可以在界面进行增删改查了。
 
2、配置并启动 seata-server (这里配置seata-server的注册中心和配置中心都使用nacos)
 
  • 修改server下的registry.config 把seata-server这个服务的注册中心和配置中心改为nacos, 后期 其他组件例如:TM,RM在配置中都使用相同的注册中心配置
          另外:把seata-server的各种配置也搬到nacos中,这个时候nacos就充当了seata-server的配置文件的动态存储中心(配置中心),PS:后期seata-client(TM,RM)也可以把它当做配置中心来使用。
          
 
  • 上个步骤已经配置「使用nacos」来作为配置中心了,那么我们接下来就得把seata-server相关的配置给初始化到配置中心去。
           问题来了: 1、seata-server 有哪些服务配置项?
                              2、如何把这些配置项放到nacos(配置中心)里面去?
           Seata的开发者也已经想到了这些问题,也已经提供了
           如下图:在script中,已经为我们提供好了相应的配置文件和初始化工具(shell脚本)
      
     我们进入对应的目录执行如下命令:
     sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t name_space_01 -u nacos -p nacos
     这个命令的意思就是,把config.txt文件中的属性配置初始化到nacos配置中心去,并且指定这些个属性配置的命名空间ID为:name_space_01(这个命名空间ID是前面nacos配置及启动步骤中,在管理界面新增的命名空间的ID)
    注:命令解析:-h -p 指定nacos的端口地址;-g 指定配置的分组,注意,是配置的分组;-t 指定命名空间id; -u -w指定nacos的用户名和密码,同样,这里开启了nacos注册和配置认证的才需要指定。
  初始化完成以后在nacos管理后台查看,如下图:
 
如果是集群模式启动,可以通过MYSQL查询其配置信息
 
 
  • 到此为主,seata-server相关的属性文件配置算是完成了,但是作为TC服务,它也是需要数据库的支持的,并且seata-server 也有它自己功能相关的表,需要新建数据库和初始化表,这个跟属性配置结合起来理解:
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc: mysql://127.0.0.1:3306/seata?useUnicode=true
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
初始化脚本:
 
  • 启动seata-server (直接执行源码的main方法),启动成功后,seata-server这个服务会自动注册到nacos(注册中心)去。
 
 
 
 
3、业务应用接入seata-client, 分别充当分布式事务中的 TM,RM角色,进行模拟调用
 
步骤一、到官网去下载样例
  这里使用的是官方的demo: springcloud-jpa-seata  ,做了一些配置修改,否则无法正常运行。修改点如下:
  1、引入nacos,做注册中心和配置中心
  2、application.properties中配置好正确的,分布式事务组。 application.properties: spring.cloud.alibaba.seata.tx-service-group= my_test_tx_group
  3、初始化数据库和表:下载的样例中有数据库脚本,去对应的myslq客户端工具执行即可。执行后数据库及表结构如下:
 
 
  借用官网的图:说明一下这几个服务之间的调用关系及分布式事务组件在全局流程中位置。
项目结构:
关键配置:这里只是把其中一个服务的配置做了展示,其他服务配置也类似就不一一的展示了
步骤二、
按如下顺序启动
  1. 启动nacos,注意:它的角色时注册中心+配置中心
  2. 启动seata-server,注意:它的角色时TC,它的配置依赖配置中心nacos中初始的各种配置:包括server独有的,和公关的
  3. 启动demo服务,demo服务启动没有向后顺序,完全启动成功以后即可进行测试。
各个阶段的日志如下:
  • Seata-server启动后会打印一些注册日志如下图:(当seata-client启动成功时,seata-server这边会有新的日志产生,下面的步骤会有截图展示)
     
  • demo中各个参与分布式事务的服务启动时,他们的日志表现,及启动后seata-server的日志表现如下:
  1. 各个服务启动日志,seata-client 相关日志如下:(这里只截取了部分)
       
  2. Seata-server的日志表现如下:由于截图屏幕有限只能截取部分,下面我就把关键日志复制出来,供大家查看:
22:55:19.267 DEBUG --- [ttyServerNIOWorker_1_1_16] i.n.util.ResourceLeakDetectorFactory     : Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@65fe92ef
22:55:19.333 DEBUG --- [ttyServerNIOWorker_1_1_16] i.s.c.rpc.netty.AbstractNettyRemoting    : io.seata.core.rpc.netty.NettyRemotingServer@13bf0c3a msgId:1, body:RegisterRMRequest{resourceIds='jdbc:mysql://localhost:3306/db_account', applicationId='account-service', transactionServiceGroup='my_test_tx_group'}
22:55:19.338 DEBUG --- [rverHandlerThread_1_1_500] i.s.c.r.processor.server.RegRmProcessor  : checkAuth for client:192.168.1.101:50756,vgroup:my_test_tx_group,applicationId:account-service is OK
22:55:19.347 DEBUG --- [rverHandlerThread_1_1_500] i.s.c.rpc.netty.AbstractNettyRemoting    : write message:version=1.4.0,extraData=null,identified=true,resultCode=null,msg=null, channel:[id: 0x8d881479, L:/192.168.1.101:8091 - R:/192.168.1.101:50756],active?true,writable?true,isopen?true
22:55:19.350  INFO --- [rverHandlerThread_1_1_500] i.s.c.r.processor.server.RegRmProcessor  : RM register success,message:RegisterRMRequest{resourceIds='jdbc:mysql://localhost:3306/db_account', applicationId='account-service', transactionServiceGroup='my_test_tx_group'},channel:[id: 0x8d881479, L:/192.168.1.101:8091 - R:/192.168.1.101:50756],client version:1.3.0
22:55:25.934 DEBUG --- [ttyServerNIOWorker_1_2_16] i.s.c.rpc.netty.AbstractNettyRemoting    : io.seata.core.rpc.netty.NettyRemotingServer@13bf0c3a msgId:1, body:RegisterRMRequest{resourceIds='jdbc:mysql://localhost:3306/db_order', applicationId='order-service', transactionServiceGroup='my_test_tx_group'}
22:55:25.935 DEBUG --- [rverHandlerThread_1_2_500] i.s.c.r.processor.server.RegRmProcessor  : checkAuth for client:192.168.1.101:50810,vgroup:my_test_tx_group,applicationId:order-service is OK
22:55:25.935 DEBUG --- [rverHandlerThread_1_2_500] i.s.c.rpc.netty.AbstractNettyRemoting    : write message:version=1.4.0,extraData=null,identified=true,resultCode=null,msg=null, channel:[id: 0x1bef0057, L:/192.168.1.101:8091 - R:/192.168.1.101:50810],active?true,writable?true,isopen?true
22:55:25.935  INFO --- [rverHandlerThread_1_2_500] i.s.c.r.processor.server.RegRmProcessor  : RM register success,message:RegisterRMRequest{resourceIds='jdbc:mysql://localhost:3306/db_order', applicationId='order-service', transactionServiceGroup='my_test_tx_group'},channel:[id: 0x1bef0057, L:/192.168.1.101:8091 - R:/192.168.1.101:50810],client version:1.3.0
22:55:28.674 DEBUG --- [ttyServerNIOWorker_1_3_16] i.s.c.rpc.netty.AbstractNettyRemoting    : io.seata.core.rpc.netty.NettyRemotingServer@13bf0c3a msgId:1, body:RegisterRMRequest{resourceIds='jdbc:mysql://localhost:3306/db_storage', applicationId='storage-service', transactionServiceGroup='my_test_tx_group'}
22:55:28.675 DEBUG --- [rverHandlerThread_1_3_500] i.s.c.r.processor.server.RegRmProcessor  : checkAuth for client:192.168.1.101:50838,vgroup:my_test_tx_group,applicationId:storage-service is OK
22:55:28.675 DEBUG --- [rverHandlerThread_1_3_500] i.s.c.rpc.netty.AbstractNettyRemoting    : write message:version=1.4.0,extraData=null,identified=true,resultCode=null,msg=null, channel:[id: 0x70eeefdd, L:/192.168.1.101:8091 - R:/192.168.1.101:50838],active?true,writable?true,isopen?true
22:55:28.676  INFO --- [rverHandlerThread_1_3_500] i.s.c.r.processor.server.RegRmProcessor  : RM register success,message:RegisterRMRequest{resourceIds='jdbc:mysql://localhost:3306/db_storage', applicationId='storage-service', transactionServiceGroup='my_test_tx_group'},channel:[id: 0x70eeefdd, L:/192.168.1.101:8091 - R:/192.168.1.101:50838],client version:1.3.0
22:55:29.265 DEBUG --- [ttyServerNIOWorker_1_1_16] i.s.c.rpc.netty.AbstractNettyRemoting    : io.seata.core.rpc.netty.NettyRemotingServer@13bf0c3a msgId:2, body:services ping
22:55:29.266 DEBUG --- [ttyServerNIOWorker_1_1_16] i.s.c.rpc.netty.AbstractNettyRemoting    : write message:services pong, channel:[id: 0x8d881479, L:/192.168.1.101:8091 - R:/192.168.1.101:50756],active?true,writable?true,isopen?true
22:55:29.267 DEBUG --- [ttyServerNIOWorker_1_1_16] i.s.c.r.p.s.ServerHeartbeatProcessor     : received PING from /192.168.1.101:50756
22:55:35.947 DEBUG --- [ttyServerNIOWorker_1_2_16] i.s.c.rpc.netty.AbstractNettyRemoting    : io.seata.core.rpc.netty.NettyRemotingServer@13bf0c3a msgId:2, body:services ping
22:55:35.948 DEBUG --- [ttyServerNIOWorker_1_2_16] i.s.c.rpc.netty.AbstractNettyRemoting    : write message:services pong, channel:[id: 0x1bef0057, L:/192.168.1.101:8091 - R:/192.168.1.101:50810],active?true,writable?true,isopen?true
22:55:35.948 DEBUG --- [ttyServerNIOWorker_1_2_16] i.s.c.r.p.s.ServerHeartbeatProcessor     : received PING from /192.168.1.101:50810
22:55:38.686 DEBUG --- [ttyServerNIOWorker_1_3_16] i.s.c.rpc.netty.AbstractNettyRemoting    : io.seata.core.rpc.netty.NettyRemotingServer@13bf0c3a msgId:2, body:services ping
22:55:38.686 DEBUG --- [ttyServerNIOWorker_1_3_16] i.s.c.rpc.netty.AbstractNettyRemoting    : write message:services pong, channel:[id: 0x70eeefdd, L:/192.168.1.101:8091 - R:/192.168.1.101:50838],active?true,writable?true,isopen?true
22:55:38.686 DEBUG --- [ttyServerNIOWorker_1_3_16] i.s.c.r.p.s.ServerHeartbeatProcessor     : received PING from /192.168.1.101:50838
22:55:39.270 DEBUG --- [ttyServerNIOWorker_1_1_16] i.s.c.rpc.netty.AbstractNettyRemoting    : io.seata.core.rpc.netty.NettyRemotingServer@13bf0c3a msgId:3, body:services ping
22:55:39.270 DEBUG --- [ttyServerNIOWorker_1_1_16] i.s.c.rpc.netty.AbstractNettyRemoting    : write message:services pong, channel:[id: 0x8d881479, L:/192.168.1.101:8091 - R:/192.168.1.101:50756],active?true,writable?true,isopen?true
22:55:39.270 DEBUG --- [ttyServerNIOWorker_1_1_16] i.s.c.r.p.s.ServerHeartbeatProcessor     : received PING from /192.168.1.101:50756
22:55:45.953 DEBUG --- [ttyServerNIOWorker_1_2_16] i.s.c.rpc.netty.AbstractNettyRemoting    : io.seata.core.rpc.netty.NettyRemotingServer@13bf0c3a msgId:3, body:services ping
22:55:45.953 DEBUG --- [ttyServerNIOWorker_1_2_16] i.s.c.rpc.netty.AbstractNettyRemoting    : write message:services pong, channel:[id: 0x1bef0057, L:/192.168.1.101:8091 - R:/192.168.1.101:50810],active?true,writable?true,isopen?true
22:55:45.954 DEBUG --- [ttyServerNIOWorker_1_2_16] i.s.c.r.p.s.ServerHeartbeatProcessor     : received PING from /192.168.1.101:50810

 

 
步骤三、根据demo中的business-service项目的接口,来做分布式事务的测试
请读者自己去调用相关接口,理解分布式事务的问题。
 
总结:
一、踩坑总结
  1. Nacos 在初始化的时候需要新建数据库并初始化MYSQL脚本,还需要配置application.properties属性文件中的数据库信息db序列,这个跟后期启动时使用的模式有关系: -m standalone 单机启动。如果不完成这些操作,启动时会报错,导致nacos启动不了。另外如果开启mysql数据库,还要配置: spring.datasource.platform = mysql 、 db.num = 1 这两个属性不配正确都会导致注册中心启动不正常,或者使用的不是MYQL存储
    大写的注意:namespace的id在后续 seata-server(TC)/seata-client(TM|RM) 的配置中都要保持一致,否则无法获取配置信息,会导致整体分布式事务无法通过正确关联。
    PS: nacos使用 -m standalone 单机模式启动时其实是使用默认的DB:Derby数据库
  2.  配置seata-server时,把其注册中心和配置中心都配成nacos(在register.conf文件中有:register{}、conf{},这两个配置块就是来指定注册中心和配置中心的),注意:当配置中心改成nacos以后需要把相当一下配置属性初始化到配置中心去,并且指定好namespace 否则,seata-server无法获取到指定的配置信息。如何初始化参数,上文已经描述具体步骤。可以参考官方文档: http://seata.io/zh-cn/docs/user/configuration/nacos.html    大写的注意:seata-server中的配置的nacos相关的命名空间:namespace一定要跟第一个步骤中nacos导入的seata配置所在的namespace相同,否则会导致获取不到配置
          
  1.    nacos作为配置中心以后:  store.db.url 、 store.db.user、 store.db.password 这几项配置是seata-server的DB配置,需要注意在使用命令导入前需要改成自己的数据库环境。
  2. 关于seata-server(TC) , seata-client(TM/RM),他们的应用配置有些地方有很密切的联系,比如:
    seata-server中的: service.vgroupMapping. my_test_tx_group =default
    在接入seata-client的服务中,其application.properties: spring.cloud.alibaba.seata.tx-service-group= my_test_tx_group
    其中所有参与分布式事务的client(TM/RM)都得标记好其事务组,并且跟seata-server的保持一致,都是自定义的: my_test_tx_group
    对于seata,配置参数很多,它们有些很关键,有些是server端独有的,有些是client端独有的,有些又是两者公共的,详细参考官方文档: http://seata.io/zh-cn/docs/user/configurations.html
  3. 在启动demo应用时,有比较多需要注意的点:1、改成nacos注册中心后,需要引入nacos的maven依赖,在启动类上加上服务发现的注解,并在application.properties中配置好注册中心的地址,服务内registry.conf文件把注册中心和配置中心都改为nacos,并指定好正确一致的namespace
  4. 在demo引入nacos的maven依赖的时候注意,springcloud-alibaba组件的版本关联关系。,比如:demo中使用的版本,跟nacos-starter要配套。并且跟spring的主版本也有关系,否则可能导致jar冲突,启动异常,遇到这种情况还请各位读者自行去解决。




    后记:如果有时间,我会考虑去深入学习一下它的源码,等到时机成熟,再出系列文章,跟大家一起分享。
    希望大家不要因为忙了,而抛弃了最初对技术的执著和热爱;工作永远都做不完,而技术学习和分享亦是永无止境。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值