96SEO 2026-02-19 12:46 3
、分布式事务存在的问题二、分布式事务理论三、认识SeataSeata分布式事务解决方案1、XA模式2、AT模式3、SAGA模式4.SAGA模式优缺点5.四种模式对比

AT案例Seata配置微服务整合2.1、父工程项目创建引入依赖
新建seata-account-service微服务2.3.2引入依赖2.3.3seata事务用到的表2.3.4seata微服务配置2.3.5yml配置seata事务2.3.6数据源交给seata去代理2.3.7主启动类去掉默认自动加载数据源2.3.8业务数据dbDOdaoservicemapper.xmlcontroller
新建seata-storage-service微服务2.4.2引入依赖2.4.3seata事务用到的表2.4.4seata微服务配置2.4.5yml配置seata事务2.4.6数据源交给seata去代理2.4.7主启动类去掉默认自动加载数据源2.4.8业务数据dbDOdaoservicemapper.xmlcontroller
新建seata-order-service微服务2.5.2引入依赖2.5.3seata事务用到的表2.5.4seata微服务配置2.5.5yml配置seata事务2.5.6数据源交给seata去代理2.5.7主启动类去掉默认自动加载数据源2.5.8业务数据dbDOdaoserviceservice实现类添加GlobalTransactionalmapper.xmlfegincontroller
在分布式系统下一个业务跨越多个服务或数据源每个服务都是一个分支事务要保证所有分支事务最终状态一致这样的事务就是分布式事务。
CAP是指Consistency(一致性Availability可用性Partition
节点1的数据一旦发生修改节点2的数据必须进行同步与节点1的数据保持一致。
用户访问集群中的任意健康节点时必须能得到响应而不是超时或者拒绝。
当节点3发生故障时就会对请求进行阻塞或者拒绝此时节点3就是不可用的。
分区因为网络故障或其它原因导致分布式系统中的部分系欸DNA与其它节点失去连接形成独立分区。
起初节点1、2、3是互相连接的即任意节点数据发生变化其余两个节点的数据也将进行同步修改但是假设节点3与其他两个节点的网络连接断开了但是节点本身并没有故障就会形成新的分区此时一旦有人对节点2的数据进行了修改并将修改信息同步到了节点1此时用户访问不同的节点假设访问节点1之后我们又访问了节点3此时拿到的结果就是不一致的。
此时就不满足一致性如果我们非要满足一致性我们怎么办让节点3进入短暂的禁止访问状态等待和节点2的网络通信回复在此期间对于到来的一切请求都进行拒绝或者阻塞这样一来确实一致性问题得到了解决但是我们又说了可用性即系统健康时就必须对请求做出响应此时我们的节点3只是和其余两个节点不通并没有不健康所以这就是一种悖论我们只能在一致性和可用性之间做出抉择
CAP定理的主要内容分布式系统节点通过网络连接一定会出现分区问题P当分区出现时系统的一致性C和可用性A就无法同时满足CP保证了系统的一致性但是牺牲了系统的可用性。
BASE理论是对CAP的一种解决思路包含三个思想Basically
基本可用分布式系统在出现故障时允许损失部分可用性即保证核心可用。
Soft
State软状态在一定时间内允许出现中间状态比如临时的不一致状态。
Eventually
Consistent最终一致性虽然无法保证强一致性但是在软状态结束后最终达到数据一致。
而分布式事务最大的问题是各个子事务的一致性问题因此可以借鉴CAP定理和BASE理论
AP模式各子事务分别执行和提交允许出现结果不一致软状态然后采用弥补措施恢复数据即可实现最终一致。
CP模式各个子事务执行后互相等待同时提交同时回滚达成强一致。
但事务等待过程中处于弱可用状态。
解决分布式事务各个子系统之间必须能感知到彼此的事务状态才能保证状态一致因此需要一个事务协调者来协调每一个事务的参与者子系统事务。
这里的子系统事务称为分支事务有关联的各个分支事务在一起称为全局事务。
事务管理器定义全局事务的范围、开始全局事务、提交或回滚全局事务。
资源管理器管理分支事务处理的资源与TC交谈以注册分支事务和报告分支事务的状态并驱动分支事务提交或回滚。
Seata分布式事务解决方案
Seata提供了四种不同的分布式事务解决方案XA模式强一致性分阶段事务模式牺牲了一定的可用性无业务侵入TCC模式最终一致的分阶段事务模式有业务侵入AT模式最终一致的分阶段事务模式无业务侵入也是Seata的默认模式SAGA模式长事务模式有业务侵入1、XA模式
描述了全局的TM与局部的RM之间的接口几乎所有主流的数据库都对
XA是规范目前主流的数据库都实现了这种规范实现的原理都是基于两阶段提交。
本地事务执行完成后向事务协调者告知事务的执行状态此时本地事务并没有提交继续持有数据库锁
事务协调者根据一阶段的报告判断下一步操作①、如果一阶段所有分支事务都执行成功则告知所有事务参与者提交事务。
②、如果一阶段任意一个事务失败则告知所有事务参与者回滚事务。
下面我们来看看Seata当中的XA模型
TC检测各分支事务的执行状态a.如果都成功通知所有RM提交事务b.如果有失败通知所有RM回滚事务RM二阶段的工作
优点事务的强一致性满足ACID原则。
常用数据库都支持实现简单并且没有代码侵入。
缺点因为一阶段需要锁定数据库资源等待二阶段结束才会释放性能较差。
依赖关系型数据库实现事务。
如何利用Seata实现XA模式
XA2、给发起全局事务的入口方法添加GlobalTransactional注解
AT模式是对XA模式的弊端进行了完善执行事务完并不会持续占有数据库资源。
我们来看看AT模式具体是怎么做的
在XA模式当中一阶段分支事务执行完事务后会等待TC的通知进行事务的提交或者事务的回滚等待过程中会持有DB锁防止其他事务进行操作这样会大大降低性能在AT模式下分支事务不需要等待TC的通知即可提交事务但是会生成一份数据快照undo_log该快照记录了数据修改前和修改后的值当TC通知分支事务进行事务提交或者回滚时如果是提交分支事务则仅需要删除生成的快照即可如果是回滚则需要根据快照当中的信息恢复数据。
XA模式一阶段不提交事务锁定资源AT模式一阶段直接提交不锁定资源。
XA模式依赖数据库机制实现回滚AT模式利用数据快照实现数据回滚。
解决方案就是引入全局锁的概念在释放DB锁之前先要拿到全局锁避免同一时刻有另外一个事务来操作当前持有的数据。
这个全局锁就限制了哪个事务可以操作该数据当事务1执行业务SQL前会先获取全局锁TC就会进行记录此时事务1释放DB锁去等待TC的通知删除快照或根据快照修复数据事务2拿到了DB锁开始执行业务SQL当事务2尝试获取全局锁时就会发现获取不到因为全局锁现在被事务1所持有此时假设恰好TC告知事务1进行数据回滚事务1则会重新获取DB锁但是DB锁此时被事务2所持有这样就形成了死锁怎么办事务2在尝试获取一段时间全局锁后一直拿不到就会放弃获取全局锁此时事务1就拿到了DB锁进行快照修复即可。
前提是两个事务均是由seata控制的事务才会这样。
假设事务2不是由seata控制的事务将会是怎样的
我们上面说过事务1记录undo_log时会记录数据修改前后的值当我们进行数据恢复时发现money此时为80此时需要我们进行报警处理人工的根据undo_log当中的数据进行数据恢复。
AT模式的优点一阶段完成直接提交事务释放数据库资源性能比较好利用全局锁实现读写隔离没有代码侵入框架自动完成回滚和提交
AT模式的缺点两阶段之间属于软状态属于最终一致框架的快照功能会影响性能但比XA模式要好很多3、TCC模式
TCC模式与AT模式非常相似每阶段都是独立事务区别在于TCC是通过人工代码控制来实现数据恢复需要实现三个方法
Cancel预留资源释放可以理解为try的反向操作。
举例一个扣减用户余额的业务。
假设账户A原来余额是100需要余额扣减30元。
阶段二Confirm)假如要提交Confirm则冻结金额扣减30
确认可以提交不过之前可用金额已经扣减过了这里只要清除冻结金额就好了
阶段二(Canncel)如果要回滚Cancel则冻结金额扣减30可用余额增加30需要回滚那么就要释放冻结金额恢复可用金额
可以看到TCC模式和AT模式极其相似但是区别在于AT模式采用全局锁undo_log的方式进行事务提交和回滚这里我们不再使用锁而是使用try-confirm-cancel的方式对操作进行记录。
当TC通知分支事务可以提交的时候分支事务直接执行confirm方法即可通知回滚时则执行cancel方法
Try资源检查和预留Confirm业务执行和提交Cancel预留资源的释放TCC的优点是什么
有代码侵入需要人为编写try、Confirm和Cancel接口太麻烦
事务悬挂对于已经空回滚的业务之前被阻塞的try操作恢复继续执行try就永远不可能confirm或cancel
执行try操作时应当判断cancel是否已经执行过了如果已经执行应当阻止空回滚后的try操作避免悬挂
空回滚当某分支事务的try阶段阻塞时可能导致全局事务超时而触发二阶段的cancel操作。
在未执行try操作时先执行了cancel操作这时cancel不能做回滚就是空回滚。
执行cancel操作时应当判断try是否已经执行如果尚未执行则应该空回
模式下分布式事务内有多个参与者每一个参与者都是一个冲正补偿服务需要用户根据业务场景实现其正向操作和逆向回滚操作。
分布式事务执行过程中依次执行各参与者的正向操作如果所有正向操作均执行成功那么分布式事务提交。
如果任何一个正向操作执行失败那么分布式事务会去退回去执行前面各参与者的逆向回滚操作回滚已提交的参与者使分布式事务回到初始状态。
分布式事务和传统形式的事务区别有什么众所周知我们采用微服务框架开发项目时不同服务之间通过相互调用的方式完成业务处理用以下案例来描述分布式业务存在的问题
假设我们此时现在有一个系统其中包括下单系统、用户系统、仓库系统。
当我们进行下单时就需要创建订单信息同时需要对用户的余额进行扣减还需要对仓储系统的商品数量进行减少由于每个系统分别负责一部分的业务且其拥有独立的数据库信息因此我们就会有三个事务订单创建、余额扣减、商品剩余量扣减。
下载Seata服务端压缩包https://github.com/seata/seata/releases
DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari)
mysql/oracle/postgresql/h2/oceanbase
jdbc:mysql://localhost:3306/seata?useUnicodetrueallowPublicKeyRetrievaltruecharacterEncodingutf-8useSSLfalseserverTimezoneGMT%2b8##
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
由于我们使用了db模式存储事务日志所以我们需要创建一个seata数据库Seata数据库表初始化脚本
--------------------------------
--------------------------------
VARCHAR(32),transaction_service_group
xmlnshttp://maven.apache.org/POM/4.0.0
xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.my.springcloud/groupIdartifactIdcloud-seata/artifactIdversion1.0-SNAPSHOT/versionpackagingpom/packagingmodulesmoduleseata-account-service/modulemoduleseata-order-service/modulemoduleseata-storage-service/modulemodulecloud-eureka-server7001/modulemodulecloud-eureka-server7002/modulemodulecloud-gateway-gateway/module/modules!--
--propertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncodingmaven.compiler.source1.8/maven.compiler.sourcemaven.compiler.target1.8/maven.compiler.targetjunit.version4.12/junit.versionlog4j.version1.2.17/log4j.versionlombok.version1.16.18/lombok.versionmysql.version8.0.28/mysql.versiondruid.version1.2.4/druid.versionmybatis.spring.boot.version2.0.0/mybatis.spring.boot.version/properties!--
子模块继承之后,提供作用锁定版本子modlue不用写groupId和version
--dependencyManagementdependencies!--spring
2.3.2--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-dependencies/artifactIdversion2.3.2.RELEASE/versiontypepom/typescopeimport/scope/dependency!--spring
Hoxton.SR9--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversionHoxton.SR9/versiontypepom/typescopeimport/scope/dependency!--spring
2.2.5.RELEASE--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-dependencies/artifactIdversion2.2.5.RELEASE/versiontypepom/typescopeimport/scope/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion${mybatis.spring.boot.version}/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion${mysql.version}/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion${druid.version}/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdversion${lombok.version}/versionoptionaltrue/optional/dependencydependencygroupIdlog4j/groupIdartifactIdlog4j/artifactIdversion${log4j.version}/version/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion${junit.version}/version/dependency!--seata依赖--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdversion2021.1/versionexclusions!--版本较低1.3.0因此排除--exclusionartifactIdseata-spring-boot-starter/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.4.1/version/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationaddResourcestrue/addResources/configuration/plugin/plugins/build
dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependency!--seata依赖--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusionartifactIdseata-all/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.4.1/version/dependency!--feign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependencies2.3.3seata事务用到的表
enableenableClientBatchSendRequest
NettyServerNIOWorkerserverExecutorThread-prefix
NettyServerBizHandlershareBossWorker
falseclientSelectorThreadPrefix
NettyClientSelectorclientSelectorThreadSize
{#这里注意,等号前后都是配置,前面是yml里配置的事务组,后面是register.conf里定义的seata-servervgroupMapping.my_test_tx_group
addressesseata_tc_server.grouplist
30retryPolicyBranchRollbackOnConflict
、eureka、redis、zk、consul、etcd3、sofa##
RandomLoadBalanceloadBalanceVirtualNodes
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/application
http://192.168.1.204:8801namespace
applicationapolloAccesskeySecret
seata-account-servicedatasource:type:
com.alibaba.druid.pool.DruidDataSource
jdbc:mysql://localhost:3306/seata_account?useUnicodetruecharacterEncodingutf8zeroDateTimeBehaviorconvertToNulluseSSLtrueserverTimezoneGMT%2B8
${spring.application.name}tx-service-group:
seata-serverenable-auto-data-source-proxy:
127.0.0.1:8091vgroup-mapping:seata-server:
所有Entity别名类所在包typeAliasesPackage:
配置mapper的扫描找到所有的mapper.xml映射文件mapperLocations:
classpath:mybatis/mybatis-config.xml#
eureka:client:#表示是否向Eureka注册中心注册自己register-with-eureka:
false表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务service-url:#defaultZone:
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/defaultZone:
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/instance:#向注册中心注册服务IDinstance-id:
${spring.cloud.client.ip-address}:${server.port}prefer-ip-address:
Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)lease-renewal-interval-in-seconds:
30#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒)超时将剔除服务lease-expiration-duration-in-seconds:
com.alibaba.druid.pool.DruidDataSource;
io.seata.rm.datasource.DataSourceProxy;
org.apache.ibatis.session.SqlSessionFactory;
org.mybatis.spring.SqlSessionFactoryBean;
org.mybatis.spring.transaction.SpringManagedTransactionFactory;
org.springframework.beans.factory.annotation.Value;
org.springframework.boot.context.properties.ConfigurationProperties;
org.springframework.context.annotation.Bean;
org.springframework.context.annotation.Configuration;
org.springframework.core.io.support.PathMatchingResourcePatternResolver;import
{//加载数据源BeanConfigurationProperties(prefix
DruidDataSource();}Value(${mybatis.mapperLocations})private
DataSourceProxy(dataSource);}Beanpublic
sqlSessionFactoryBean(DataSource
SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new
PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new
SpringManagedTransactionFactory());return
sqlSessionFactoryBean.getObject();}}2.3.7主启动类去掉默认自动加载数据源
org.springframework.boot.SpringApplication;
org.springframework.boot.autoconfigure.SpringBootApplication;
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
org.springframework.cloud.netflix.eureka.EnableEurekaClient;
org.springframework.cloud.openfeign.EnableFeignClients;EnableEurekaClient
DataSourceAutoConfiguration.class)
{SpringApplication.run(SeataAccountApp.class,
http://mybatis.org/dtd/mybatis-3-mapper.dtd
namespacecom.my.springcloud.dao.AccountDaoresultMap
typecom.my.springcloud.domain.Accountid
jdbcTypeDECIMAL//resultMapupdate
扣减账户余额*/RequestMapping(/account/decrease)public
{accountService.decrease(userId,
dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependency!--seata依赖--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusionartifactIdseata-all/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.4.1/version/dependency!--feign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependencies2.4.3seata事务用到的表
enableenableClientBatchSendRequest
NettyServerNIOWorkerserverExecutorThread-prefix
NettyServerBizHandlershareBossWorker
falseclientSelectorThreadPrefix
NettyClientSelectorclientSelectorThreadSize
{#这里注意,等号前后都是配置,前面是yml里配置的事务组,后面是register.conf里定义的seata-servervgroupMapping.my_test_tx_group
addressesseata_tc_server.grouplist
30retryPolicyBranchRollbackOnConflict
、eureka、redis、zk、consul、etcd3、sofa##
RandomLoadBalanceloadBalanceVirtualNodes
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/application
http://192.168.1.204:8801namespace
applicationapolloAccesskeySecret
seata-storage-service#----------------------------数据源----------------------------datasource:type:
com.alibaba.druid.pool.DruidDataSourcedriverClassName:
jdbc:mysql://localhost:3306/seata_storage?useUnicodetruecharacterEncodingutf8zeroDateTimeBehaviorconvertToNulluseSSLtrueserverTimezoneGMT%2B8username:
truemax-pool-prepared-statement-per-connection-size:
60000time-between-eviction-runs-millis:
60000min-evictable-idle-time-millis:
true#----------------------------seata----------------------------
${spring.application.name}tx-service-group:
seata-serverenable-auto-data-source-proxy:
127.0.0.1:8091vgroup-mapping:seata-server:
eureka:client:#表示是否向Eureka注册中心注册自己register-with-eureka:
false表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务service-url:#defaultZone:
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/defaultZone:
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/instance:#向注册中心注册服务IDinstance-id:
${spring.cloud.client.ip-address}:${server.port}prefer-ip-address:
Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)lease-renewal-interval-in-seconds:
30#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒)超时将剔除服务lease-expiration-duration-in-seconds:
mybatis:#所有Entity别名类所在包,多个路径用逗号分割typeAliasesPackage:
配置mapper的扫描找到所有的mapper,多个目录用逗号或者分号分隔mapperLocations:
classpath:mybatis/mybatis-config.xml
com.alibaba.druid.pool.DruidDataSource;
io.seata.rm.datasource.DataSourceProxy;
org.apache.ibatis.session.SqlSessionFactory;
org.mybatis.spring.SqlSessionFactoryBean;
org.mybatis.spring.transaction.SpringManagedTransactionFactory;
org.springframework.beans.factory.annotation.Value;
org.springframework.boot.context.properties.ConfigurationProperties;
org.springframework.context.annotation.Bean;
org.springframework.context.annotation.Configuration;
org.springframework.core.io.support.PathMatchingResourcePatternResolver;import
{//加载数据源BeanConfigurationProperties(prefix
DruidDataSource();}Value(${mybatis.mapperLocations})private
DataSourceProxy(dataSource);}Beanpublic
sqlSessionFactoryBean(DataSource
SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new
PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new
SpringManagedTransactionFactory());return
sqlSessionFactoryBean.getObject();}}2.4.7主启动类去掉默认自动加载数据源
DataSourceAutoConfiguration.class)
{SpringApplication.run(SeataStorageApp.class,
http://mybatis.org/dtd/mybatis-3-mapper.dtd
namespacecom.my.springcloud.dao.StorageDaoresultMap
typecom.my.springcloud.domain.Storageid
jdbcTypeINTEGER//resultMapupdate
扣减库存*/RequestMapping(/storage/decrease)public
{storageService.decrease(productId,
dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependency!--seata依赖--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusionartifactIdseata-all/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.4.1/version/dependency!--feign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!--
--dependencygroupIdio.github.openfeign/groupIdartifactIdfeign-httpclient/artifactId/dependencydependencygroupIdio.github.openfeign/groupIdartifactIdfeign-okhttp/artifactId/dependency/dependencies2.5.3seata事务用到的表
enableenableClientBatchSendRequest
NettyServerNIOWorkerserverExecutorThread-prefix
NettyServerBizHandlershareBossWorker
falseclientSelectorThreadPrefix
NettyClientSelectorclientSelectorThreadSize
{#这里注意,等号前后都是配置,前面是yml里配置的事务组,后面是register.conf里定义的seata-servervgroupMapping.my_test_tx_group
addressesseata_tc_server.grouplist
30retryPolicyBranchRollbackOnConflict
、eureka、redis、zk、consul、etcd3、sofa##
RandomLoadBalanceloadBalanceVirtualNodes
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/application
http://192.168.1.204:8801namespace
applicationapolloAccesskeySecret
seata-order-service#----------------------------数据源----------------------------datasource:type:
com.alibaba.druid.pool.DruidDataSourcedriverClassName:
jdbc:mysql://localhost:3306/seata_order?useUnicodetruecharacterEncodingutf8zeroDateTimeBehaviorconvertToNulluseSSLtrueserverTimezoneGMT%2B8username:
truemax-pool-prepared-statement-per-connection-size:
60000time-between-eviction-runs-millis:
60000min-evictable-idle-time-millis:
true#----------------------------seata----------------------------
${spring.application.name}tx-service-group:
seata-serverenable-auto-data-source-proxy:
127.0.0.1:8091vgroup-mapping:seata-server:
eureka:client:#表示是否向Eureka注册中心注册自己register-with-eureka:
false表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务service-url:#defaultZone:
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/defaultZone:
http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/instance:#向注册中心注册服务IDinstance-id:
${spring.cloud.client.ip-address}:${server.port}prefer-ip-address:
Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)lease-renewal-interval-in-seconds:
30#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒)超时将剔除服务lease-expiration-duration-in-seconds:
mybatis:#所有Entity别名类所在包,多个路径用逗号分割typeAliasesPackage:
配置mapper的扫描找到所有的mapper,多个目录用逗号或者分号分隔mapperLocations:
classpath:mybatis/mybatis-config.xml#
日志级别BASIC就是基本的请求和响应信息httpclient:enabled:
开启feign对HttpClient的支持max-connections:
最大的连接数max-connections-per-route:
每个路径的最大连接数logging:level:io:seata:
com.alibaba.druid.pool.DruidDataSource;
io.seata.rm.datasource.DataSourceProxy;
org.apache.ibatis.session.SqlSessionFactory;
org.mybatis.spring.SqlSessionFactoryBean;
org.mybatis.spring.transaction.SpringManagedTransactionFactory;
org.springframework.beans.factory.annotation.Value;
org.springframework.boot.context.properties.ConfigurationProperties;
org.springframework.context.annotation.Bean;
org.springframework.context.annotation.Configuration;
org.springframework.core.io.support.PathMatchingResourcePatternResolver;import
{//加载数据源BeanConfigurationProperties(prefix
DruidDataSource();}Value(${mybatis.mapperLocations})private
DataSourceProxy(dataSource);}Beanpublic
sqlSessionFactoryBean(DataSource
SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new
PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new
SpringManagedTransactionFactory());return
sqlSessionFactoryBean.getObject();}}2.5.7主启动类去掉默认自动加载数据源
DataSourceAutoConfiguration.class)//取消数据源的自动创建
{SpringApplication.run(SeataOrderApp.class,
service实现类添加GlobalTransactional
创建订单-调用库存服务扣减库存-调用账户服务扣减账户余额-修改订单状态*
简单说下订单-扣库存-减余额-改状态*/OverrideGlobalTransactional(
扣减库存log.info(-----订单微服务开始调用库存做扣减Count);storageService.decrease(order.getProductId(),
order.getCount());log.info(-----订单微服务开始调用库存做扣减end);//3
扣减账户log.info(-----订单微服务开始调用账户做扣减Money);accountService.decrease(order.getUserId(),
order.getMoney());log.info(-----订单微服务开始调用账户做扣减end);//4
修改订单状态从零到1,1代表已经完成log.info(-----修改订单状态开始);orderDao.update(order.getUserId(),
0);log.info(-----修改订单状态结束);log.info(-----下订单结束了O(∩_∩)O哈哈~);}
http://mybatis.org/dtd/mybatis-3-mapper.dtd
namespacecom.my.springcloud.dao.OrderDaoresultMap
typecom.my.springcloud.domain.Orderid
jdbcTypeINTEGER//resultMapinsert
decrease(RequestParam(productId)
orderService;GetMapping(/order/create)public
{orderService.create(order);return
http://localhost:2001/order/create
添加GlobalTransactional注解开启seata事务
GlobalTransactionContext.reload(xid);
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback