apollo和nacos区别,apollo3芯片规格书

  

  我们前面提到了Seata的AT和TCC模式。本文针对这两种模式进行了深入的分析和开发实践。   

  

  AT模式原理综述   

  

  根据官方文档和提供的博客,我们先来回顾一下AT模式下分布式事务的原理。   

  

  AT模式的第一阶段和第二阶段提交和回滚由Seata框架自动生成,用户可以通过编写“业务SQL”轻松访问分布式事务。AT模式是一种分布式交易解决方案,对业务没有任何入侵。   

  

     

  

  第一阶段:   

  

  第一阶段,Seata会拦截业务SQL,先分析SQL语义,找到业务SQL要更新的业务数据,在业务数据更新前保存为“前像”,然后执行业务SQL更新业务数据,业务数据更新后保存为“后像”,最后生成行锁。上述所有操作都在一个数据库事务中完成,从而保证了第一阶段操作的原子性。   

  

  两阶段提交:   

  

  第二阶段,如果提交,由于第一阶段已经将“业务SQL”提交给数据库,Seata框架只需要删除第一阶段保存的快照数据和行锁就可以完成数据清洗。   

  

     

  

  两阶段回滚:   

  

  如果第二阶段是回滚,Seata需要回滚在第一阶段执行的“业务SQL”来恢复业务数据。回滚方法是使用“前映像”恢复业务数据;但是,在恢复之前,需要验证脏写,并将“数据库的当前业务数据”与“镜像后”进行比较。如果两个数据完全一致,说明没有脏写,可以恢复业务数据。如果有不一致,说明有脏写,需要手动处理。   

  

     

  

  环境建设   

  

  本演示中使用的环境基于   

  

  春天的云   

  

  首先,在服务器上构建seata-server。由于我们使用nacos作为seata的注册中心,使用apollo作为注册中心,所以我们将首先构建这两个组件。具体安装方法请参考各自的官方文档。纳科斯阿波罗   

  

  nacos和apollo在一起后,我们开始构建seata-server。以下是docker-compose的配置:   

  

  版本: '3.1 '服务: seata-server:映像: seataio/Seata-server3360最新主机名3360 seata-server端口3360-809133608091环境3360-Seata _ port=8091-Seata _ IP={ your IP }-Seata _ CONFIG _ NAME=file :/Seata-server/resources/registry volumes :-。/seata/registry . conf :/seata-server/resources/registry . conf expos :-8091   

  

  修改registry.conf配置文件。因为我们使用nacos作为注册中心,使用apollo作为配置中心,所以需要将它修改为以下配置:   

  

  注册表{ # file、nacos、eureka、redis、zk、consul、etcd3、Sofa type=' nacos ' load balance=' randomloadbalance ' load balance虚拟节点=10 nacos { application=' SEATA-server ' server addr=' your IP : port ' GROUP p=' SEATA _ GROUP ' namespace=' ' cluster=' default ' username=' ' password=' ' } }配置{ # file、nacos、apollo、zk、consul、etcd3类型   

= "apollo" apollo { appId = "seata-server" apolloMeta = "http://你的IP:端口" namespace = "application" env= "dev" apolloAccesskeySecret = "" } }

  

注意:seata-server 是可以配置数据库存储 seata 所用数据的,我们为了方便利用本地 file 的方式存储数据,所以没有再做数据库的配置。如需修改可以修改配置文件 file.conf

  


  

下面是 file.conf 的默认配置:

  


  

store { ## store mode: file、db、redis mode = "file" ## file store property file { ## store location dir dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions maxBranchSessionSize = 16384 # globe session size , if exceeded throws exceptions maxGlobalSessionSize = 512 # file buffer size , if exceeded allocate new buffer fileWriteBufferCacheSize = 16384 # when recover batch read size sessionReloadReadSize = 100 # async, sync flushDiskMode = async } ## database store property db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. datasource = "druid" ## mysql/oracle/postgresql/h2/oceanbase etc. dbType = "mysql" driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://127.0.0.1:3306/seata" user = "mysql" password = "mysql" minConn = 5 maxConn = 100 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } ## redis store property redis { host = "127.0.0.1" port = "6379" password = "" database = "0" minConn = 1 maxConn = 10 maxTotal = 100 queryLimit = 100 }}

  

启动 nacos、apollo、seata-server

  


  

当显示以下信息时,代表seata-server启动了。

  

  


  

这时我们查看 nacos ,也注册上了

  

  


  

apollo中我们添加一个名为 service.vgroup-mapping.demo-service-seata的key ,value为 default,至于这个的作用,我们后面再说。

  

  


  

我们的 demo 中包含三个服务

  


  

demo-orderdemo-storagedemo-user

  

服务间调用使用的是Spring Cloud OpenFeign,除了 SpringBoot 和Spring Cloud 等基础 bom 要依赖外,还需要加入 seata 的依赖,我的pom,大致如下:

  


  

<properties> <spring-boot-dependencies.version>2.3.2.RELEASE</spring-boot-dependencies.version> <spring-cloud-dependencies.version>Hoxton.SR8</spring-cloud-dependencies.version> <spring-cloud-alibaba-dependencies.version>2.2.3.RELEASE</spring-cloud-alibaba-dependencies.version></properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring-boot-dependencies.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud-dependencies.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba-dependencies.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- 实现对 Spring MVC 的自动化配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入 Spring Cloud Alibaba Seata 相关依赖,使用 Seata 实现分布式事务,并实现对其的自动配置 --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中心,并实现对其的自动配置 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- 引入 Spring Cloud OpenFeign 相关依赖,使用 OpenFeign 提供声明式调用,并实现对其的自动配置 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>

  

至于项目中所用ORM框架,数据库连接池等就因人而异了,我用的是mybatis-plus和hikari,数据库用的是 mysql5.7。

  


  

针对上面的三个服务分别创建三个数据库,order、user、storage,并在每个库中分别创建一个业务表 t_order、t_user、t_storage 这里就不贴建库表的脚本了,大家可以按照自己的设计自己建,需要注意的是每个库都需要再创建一个 undo_log 表,这是为seata做分布式事务回滚所用。

  


  

CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

  

每个服务中 application.yml 中对应 seata 的配置如下

  


  

spring: profiles: active: dev cloud: nacos: discovery: namespace: public password: nacos server-addr: IP:PORT networkInterface: eth1 username: nacos# Seata 配置项,对应 SeataProperties 类seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: demo-service-seata # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: demo-service-seata: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: 你的IP:端口 # Nacos 服务地址

  

这里有几点需要注意:

  


  

demo-service-seata 出现了两次,这两个地方要写成一样demo-service-seata: default与我们在 apollo 中配置的要一样与 seata-server registry.conf 中 nacos 的 cluster 配置一样。nacos 配置 networkInterface: eth1这样写是因为服务部署在服务器后用的内网IP注册到了nacos,想配置它用外网地址就改了下走特定网卡。解决方案参考:这里
例如,使用了Spring cloud alibaba(官方文档)作为Nacos客户端,服务默认获取了内网IP 192.168.1.21,可以通过配置 spring.cloud.inetutils.preferred-networks=10.34.12,使服务获取内网中前缀为10.34.12的IP在老版本的 seata 是需要手动设置 DataSourceProxy的 ,参考 官网文档 新版本的默认是自动代理的,不需要再写了。

  

至此我们的环境搭建和准备工作就结束了。

  


  

分布式事务具体代码

  

我们设计这样一个同步的业务流程,创建订单前先扣减库存,再扣减账户余额,然后再创建订单,demo设计上参考了 芋道源码。大致流程如下图:

  

  


  

  


通过入口进入orderServicer后,进行上面的三步流程,分别调用两个微服务,再调自己的订单服务,这里注意两点:

  


  

分布式全局事务入口,要添加 @GlobalTransactional要抛出异常

  

接下来是扣减库存微服务部分,简单做了下扣减,小于10抛出异常

  

  


  

然后是账户微服务部分

  

  


  

最后是订单

  

  


  

代码都比较简单,有几个点需要注意下

  


  

全局事务的隔离性和本地事务的不是一个概念。全局事务的隔离级别一定基础上依赖本地事务的隔离级别。因此本地事务的隔离级别只要大于等于seata支持的隔离级别就行,所以一般数据库的默认级别就可以seata的全局事务注解是@GlobalTransactional,@Transactional 是spring的注解,解决本地事务问题,属于两种不同粒度的事务范畴。如果要加全局事务就一定要用 @GlobalTransactional。在一个事务方法上,是可以叠加两个注解的,仅意味着功能的叠加,即:有本地事务的处理,也有全局事务的加持。两者不冲突。

  

由于在数据库本地事务隔离级别 已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 未提交(Read Uncommitted)

  


  

所以这种隔离性会带来问题(注意这里说的是全局事务):

  


  

脏读: 一个事务读取到另一个事务未提交的数据
解决方案:@GlobalLock+@Transactional 注解 + select语句加for update 或GlobalTransactional注解+select语句加for update脏写: 一个事务提交的数据覆盖了另一个事务未提交的数据
解决方案:必须使用@GlobalTransaction

  

其实上面这部分,官方文档也写的很清楚,尤其对于隔离性的解析:

  

  


  

上图有些地方理解起来要注意:

  


  

这里说的事务指的是全局的分布式事务,别想成本地事务了,关于@GlobalLock,场景是一个是全局分布式事务,另一个不是分布式事务,如果你想让分布式事务不产生“脏读”,那么可以在另一个非分布式事务上加@GlobalLock。

  

我的测试中事务的正常执行和回滚都没有问题,如果你观察各数据库的 undo_log 表,可能会发现没有数据,但实际情况是数据是插入后又很快清除了,所以你没看到,如果你观察主键的 auto_increment 可以看到一直在增长。由于我用了阿里云的RDS,可以通过SQL洞察看到SQL的执行历史,这里看到sql确实执行过。

  

  


  

XID是全局事务ID,有时候我们需要获得并进行一些操作,那么可以这样做

  


  

String xid = RootContext.getXID();RootContext.unbind();//解绑//中途做一些与事务无关的事。比如日志服务等等 排除掉,然后RootContext.bind(xid);//再绑回来

  

@GlobalTransactional也有自己的隔离级别和rollback等,可根据业务情况自行设置

  


  

package io.seata.spring.annotation;import io.seata.tm.api.transaction.Propagation;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD, ElementType.TYPE})@Inheritedpublic @interface GlobalTransactional { int timeoutMills() default 60000; String name() default ""; Class<? extends Throwable><> rollbackFor() default {}; String<> rollbackForClassName() default {}; Class<? extends Throwable><> noRollbackFor() default {}; String<> noRollbackForClassName() default {}; Propagation propagation() default Propagation.REQUIRED;}

  

AT 总结

  

再次强调AT模式是自动的,它自动帮你做回滚和提交,使用时考虑跟自己的实际业务场景是否适合。例子中我对执行事务的方法并没有做幂等,在实际生产情况下,一定会出现问题的,所以大家在用的时候要注意做接口幂等处理。有关更多seata的参数配置,如超时,重试次数等。请参考 官网 。这里当然要结合你的feign的重试和超时时间整体考虑。通过上文的描述我们利用一个例子将AT模式的全局分布式事务模拟了出来,也总结了一些比较难理解和需要注意的点,希望能够帮助到正在使用seata的小伙伴。

  

参考资料<1>

  

seata官方文档:http://seata.io/zh-cn/docs/overview/what-is-seata.html

  

<2>

  

分布式事务 Seata 及其三种模式详解:http://seata.io/zh-cn/blog/seata-at-tcc-saga.html

  

<3>

  

nacos官方文档:https://nacos.io/zh-cn/

  

<4>

  

apollo的github地址:https://github.com/ctripcorp/apollo

  

<5>

  

解决nacos注册内网地址问题:https://www.cnblogs.com/liboware/p/11973321.html

  

<6>

  

官网文档:http://seata.io/zh-cn/docs/user/configurations.html

  

<7>

  

芋道源码:http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/

  

<8>

  

官网参数配置:http://seata.io/zh-cn/docs/user/configurations.html

相关文章