当前位置: 首页 > news >正文

青岛做网站的公司哪个好修改数据库密码 进不了网站后台

青岛做网站的公司哪个好,修改数据库密码 进不了网站后台,wordpress视频点播,wordpress中级课程凌风主讲一、前言 接下来是开展一系列的 SpringCloud 的学习之旅#xff0c;从传统的模块之间调用#xff0c;一步步的升级为 SpringCloud 模块之间的调用#xff0c;此篇文章为第十八篇#xff0c;即使用 Seata 处理分布式事务。 二、分布式事务问题 当单体应用被拆分成微服务应用…一、前言 接下来是开展一系列的 SpringCloud 的学习之旅从传统的模块之间调用一步步的升级为 SpringCloud 模块之间的调用此篇文章为第十八篇即使用 Seata 处理分布式事务。 二、分布式事务问题 当单体应用被拆分成微服务应用时假设原来由三个模块构成的一个单体应用就会被拆分成三个独立的应用分别使用三个独立的数据源以下图为例 用户购买商品的业务逻辑整个业务逻辑由三个微服务提供支持。仓储服务对给定的商品扣除仓储数量订单服务根据采购需求创建订单账户服务从用户账户中扣除余额。 总结起来就是一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用就会产生分布式事务问题。 三、Seata 简介 3.1 Seata 是什么 Seata 是一款开源的分布式事务解决方案致力于在微服务架构下提供高性能和简单易用的分布式事务服务。官网地址在这 3.2 Seata 作用 一个典型的分布式事务过程是由分布式事务处理过程的一ID 三组件模型构成。 Transaction ID XID全局唯一的事务 ID Transaction Coordinator (TC)事务协调器维护全局事务的运行状态负责协调并驱动全局事务的提交或回滚。 Transaction Manager (TM)事务管理器定义全局事务的范围开始全局事务、提交或回滚全局事务。 Resource Manager (RM)资源管理器管理分支事务处理的资源与 TC 交谈以注册分支事务和报告分支事务的状态并驱动分支事务提交或回滚。 3.3 处理过程 1、TM 向 TC 申请开启一个全局事务全局事务创建成功并生成一个全局唯一的 XID 2、XID 在微服务调用链路的上下文中传播 3、RM 向 TC 注册分支事务将其纳入 XID 对应全局事务的管辖 4、TM 向 TC 发起针对 XID 的全局提交或回滚决议。 5、TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。 四、Seata-Server 安装 4.1 下载 在官网选择适合自己的版本我这边下载的是 seata-server-0.9.0.zip 版本最好跟我一样 4.2 安装 首先将下载好的安装包解压到指定目录并修改 conf 目录下的 file.conf 配置文件一共修改两个地方第一个地方是修改 service 模块自定义事务组的名称叫什么都可以如下图 第二个地方是修改 store 模块事务日志存储模式为 db 和数据库连接信息如下图 接下来在 mysql5.7 数据库新建库 seata然后运行 conf 目录下的 db_store.sql 脚本如下图 执行完 sql 脚本之后会创建三张表如下图  接下来修改 conf 目录下的 registry.conf 配置文件指明注册中心为 nacos并修改 nacos 的连接信息如下图 最后先启动 Nacos然后在 bin 目录下点击 seata-server.bat 命令来启动 seata启动成功的界面如下所示 五、数据库准备 5.1 业务说明 接下来我们模拟一个 seata 的分布式交易解决方案的场景如下图 这里我们会创建三个服务一个订单服务一个库存服务和一个账户服务。 当用户下单时会在订单服务中创建一个订单然后通过远程调用库存服务来扣减下单商品的库存再通过远程调用账户服务来扣减用户账户里面的余额最后在订单服务中修改订单状态为已完成。 该操作跨越三个数据库有两次远程调用很明显会有分布式事务问题。 总结起来就是下订单 ---- 扣库存 ---- 减账户 5.2 创建数据库 我们需要创建三个数据库分别为seata_order 存储订单的数据库seata_storage 存储库存的数据库seata_account 存储账户信息的数据库。建表语句如下 create database seata_order; create database seata_storage; create database seata_account; 5.3 创建表 在 seata_order 库下创建 t_order 表建表语句如下 drop TABLE if EXISTS t_order; CREATE TABLE t_order (id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,user_id BIGINT(11) DEFAULT NULL COMMENT 用户id,product_id BIGINT(11) DEFAULT NULL COMMENT 产品id,count INT(11) DEFAULT NULL COMMENT 数量,money DECIMAL(11,0) DEFAULT NULL COMMENT 金额,status INT(1) DEFAULT NULL COMMENT 订单状态: 0: 创建中;1: 已完结 )ENGINEINNODB AUTO_INCREMENT7 DEFAULT CHARSETutf8;在 seata_storage 库下创建 t_storage 表建表语句如下 drop TABLE if EXISTS t_storage; CREATE TABLE t_storage (id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,product_id BIGINT(11) DEFAULT NULL COMMENT 产品id,total INT(11) DEFAULT NULL COMMENT 总库存,used INT(11) DEFAULT NULL COMMENT 已用库存,residue INT(11) DEFAULT NULL COMMENT 剩余库存 )ENGINE INNODB AUTO_INCREMENT2 DEFAULT CHARSETutf8; INSERT INTO t_storage(id, product_id,total,used,residue)VALUES (1,1,100,0,100); 在 seata_account 库下创建 t_account 表建表语句如下 drop TABLE if EXISTS t_account; CREATE TABLE t_account (id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT id,user_id BIGINT(11) DEFAULT NULL COMMENT 用户id,total DECIMAL(10,0) DEFAULT NULL COMMENT 总额度,used DECIMAL(10.0) DEFAULT NULL COMMENT 已用余,residue DECIMAL(10,0) DEFAULT 0 COMMENT 剩余可用额度 )ENGINEINNODB AUTO_INCREMENT2 DEFAULT CHARSETutf8; INSERT INTO t_account( id , user_id , total,used,residue) VALUES (1,1,1000,0,1000);最后在上面创建的三个数据库下分别创建各自的回滚日志表执行的语句在 conf 目录下的 db_undo_log.sql如下图 最终的效果如下图 六、订单微服务准备 创建一个 seata-order-service2001 的订单微服务模块接下来详细的介绍下模块的相关内容。 父 pom.xml 的内容如下所示 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns: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.springcloud/groupIdartifactIdSpringCloud/artifactIdversion1.0-SNAPSHOT/versionpackagingpom/packagingmodulesmodulecloud-provider-payment8001/modulemodulecloud-consumer-order80/modulemodulecloud-api-commons/modulemodulecloud-eureka-server7001/modulemodulecloud-eureka-server7002/modulemodulecloud-provider-payment8002/modulemodulecloud-provider-payment8004/modulemodulecloud-consumerzk-order80/modulemodulecloud-provider-payment8006/modulemodulecloud-consumerconsul-order80/modulemodulecloud-consumer-feign-order80/modulemodulecloud-provider-hystrix-payment8001/modulemodulecloud-consumer-feign-hystrix-order80/modulemodulecloud-consumer-hystrix-dashboard9001/modulemodulecloud-gateway-gateway9527/modulemodulecloud-config-center-3344/modulemodulecloud-config-client-3355/modulemodulecloud-config-center-3366/modulemodulecloud-stream-rabbitmq-provider8801/modulemodulecloud-stream-rabbitmq-consumer8802/modulemodulecloud-stream-rabbitmq-consumer8803/modulemodulecloudalibaba-provider-payment9001/modulemodulecloudalibaba-provider-payment9002/modulemodulecloudalibaba-consumer-nacos-order83/modulemodulecloudalibaba-config-nacos-client3377/modulemodulecloudalibaba-sentinel-service8401/modulemodulecloudalibaba-provider-payment9003/modulemodulecloudalibaba-provider-payment9004/modulemodulecloudalibaba-consumer-nacos-order84/modulemoduleseata-order-service2001/modulemoduleseata-storage-service2002/modulemoduleseata-account-service2003/module/modules!-- 统一管理jar包版本 --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.version5.1.47/mysql.versiondruid.version1.1.16/druid.versionmybatis.spring.boot.version1.3.0/mybatis.spring.boot.version/properties!-- 子模块继承之后提供作用锁定版本子modlue不用写groupId和version --dependencyManagementdependencies!--spring boot 2.2.2--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-dependencies/artifactIdversion2.2.2.RELEASE/versiontypepom/typescopeimport/scope/dependency!--spring cloud Hoxton.SR1--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversionHoxton.SR1/versiontypepom/typescopeimport/scope/dependency!--spring cloud alibaba 2.1.0.RELEASE--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-dependencies/artifactIdversion2.1.0.RELEASE/versiontypepom/typescopeimport/scope/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion${mysql.version}/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion${druid.version}/version/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion${mybatis.spring.boot.version}/version/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion${junit.version}/version/dependencydependencygroupIdlog4j/groupIdartifactIdlog4j/artifactIdversion${log4j.version}/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdversion${lombok.version}/versionoptionaltrue/optional/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationaddResourcestrue/addResources/configuration/plugin/plugins/build /project 6.1 添加 maven 依赖 pom.xml 的内容如下所示唯一需要注意的是需要移除 spring-cloud-starter-alibaba-seata 依赖里面包含的 seata 依赖单独引入 seata 依赖版本不兼容。模块之间的调用使用 openfeign。 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns: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/modelVersionparentgroupIdcom.springcloud/groupIdartifactIdSpringCloud/artifactIdversion1.0-SNAPSHOT/version/parentartifactIdseata-order-service2001/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/propertiesdependencies!--nacos--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency!--seata--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusionartifactIdseata-all/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-all/artifactIdversion0.9.0/version/dependency!--feign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency!--web-actuator--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-actuator/artifactId/dependency!--mysql-druid--dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.37/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion1.1.10/version/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion2.0.0/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency/dependencies /project 6.2 创建 application.yml 在 resources 目录下创建 application.yml 文件内容如下所示唯一需要注意的是自定义事务组名称需要与 seata-server 中的对应即本次的事务组要和我们的 seata 服务器的组要匹配。 server:port: 2001spring:application:name: seata-order-servicecloud:alibaba:seata:# 自定义事务组名称需要与 seata-server 中的对应tx-service-group: xhf_tx_groupnacos:discovery:server-addr: localhost:8848datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_orderusername: rootpassword: 123456feign:hystrix:enabled: falselogging:level:io:seata: infomybatis:mapperLocations: classpath:mapper/*.xml 6.3 创建 file.conf 在 resources 目录下创建 file.conf 文件内容如下所示注意修改 service 和 db 模块的相关信息。这个文件是控制该微服务的我们还有 seata-server 里面还有一个 file.conf 是负责总控的。 transport {# tcp udt unix-domain-sockettype TCP#NIO NATIVEserver NIO#enable heartbeatheartbeat true#thread factory for nettythread-factory {boss-thread-prefix NettyBossworker-thread-prefix NettyServerNIOWorkerserver-executor-thread-prefix NettyServerBizHandlershare-boss-worker falseclient-selector-thread-prefix NettyClientSelectorclient-selector-thread-size 1client-worker-thread-prefix NettyClientWorkerThread# netty boss thread size,will not be used for UDTboss-thread-size 1#auto default pin or 8worker-thread-size 8}shutdown {# when destroy server, wait secondswait 3}serialization seatacompressor none }service {vgroup_mapping.xhf_tx_group default #修改自定义事务组名称default.grouplist 127.0.0.1:8091enableDegrade falsedisable falsemax.commit.retry.timeout -1max.rollback.retry.timeout -1disableGlobalTransaction false }client {async.commit.buffer.limit 10000lock {retry.internal 10retry.times 30}report.retry.count 5tm.commit.retry.count 1tm.rollback.retry.count 1 }## transaction log store store {## store mode: file、dbmode db## file storefile {dir sessionStore# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptionsmax-branch-session-size 16384# globe session size , if exceeded throws exceptionsmax-global-session-size 512# file buffer size , if exceeded allocate new bufferfile-write-buffer-cache-size 16384# when recover batch read sizesession.reload.read_size 100# async, syncflush-disk-mode async}## database storedb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.datasource dbcp## mysql/oracle/h2/oceanbase etc.db-type mysqldriver-class-name com.mysql.jdbc.Driverurl jdbc:mysql://127.0.0.1:3306/seatauser rootpassword 123456min-conn 1max-conn 3global.table global_tablebranch.table branch_tablelock-table lock_tablequery-limit 100} } lock {## the lock store mode: local、remotemode remotelocal {## store locks in users database}remote {## store locks in the seatas server} } recovery {#schedule committing retry period in millisecondscommitting-retry-period 1000#schedule asyn committing retry period in millisecondsasyn-committing-retry-period 1000#schedule rollbacking retry period in millisecondsrollbacking-retry-period 1000#schedule timeout retry period in millisecondstimeout-retry-period 1000 }transaction {undo.data.validation trueundo.log.serialization jacksonundo.log.save.days 7#schedule delete expired undo_log in millisecondsundo.log.delete.period 86400000undo.log.table undo_log }## metrics settings metrics {enabled falseregistry-type compact# multi exporters use comma dividedexporter-list prometheusexporter-prometheus-port 9898 }support {## springspring {# auto proxy the DataSource beandatasource.autoproxy false} } 6.4 创建 registry.conf 在 resources 目录下创建 registry.conf 文件内容如下所示这个配置文件用于标注出我们的这个微服务是向哪个注册中心进行注册的。这个文件是控制该微服务的我们还有 seata-server 里面还有一个 registry.conf 是负责总控的。 registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype nacosnacos {serverAddr localhost:8848namespace cluster default}eureka {serviceUrl http://localhost:8761/eurekaapplication defaultweight 1}redis {serverAddr localhost:6379db 0}zk {cluster defaultserverAddr 127.0.0.1:2181session.timeout 6000connect.timeout 2000}consul {cluster defaultserverAddr 127.0.0.1:8500}etcd3 {cluster defaultserverAddr http://localhost:2379}sofa {serverAddr 127.0.0.1:9603application defaultregion DEFAULT_ZONEdatacenter DefaultDataCentercluster defaultgroup SEATA_GROUPaddressWaitTime 3000}file {name file.conf} }config {# file、nacos 、apollo、zk、consul、etcd3type filenacos {serverAddr localhostnamespace }consul {serverAddr 127.0.0.1:8500}apollo {app.id seata-serverapollo.meta http://192.168.1.204:8801}zk {serverAddr 127.0.0.1:2181session.timeout 6000connect.timeout 2000}etcd3 {serverAddr http://localhost:2379}file {name file.conf} } 6.5 创建实体类 首先创建一个用于返回前端的调用结果的实体类 CommonResult代码如下 package com.springcloud.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;// 用于返回前端的调用结果 Data NoArgsConstructor AllArgsConstructor public class CommonResult T{private Integer code;private String message;private T data;public CommonResult(Integer code,String message){this(code,message,null);} } 然后创建订单的实体类 Order代码如下 package com.springcloud.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.math.BigDecimal;Data NoArgsConstructor AllArgsConstructor public class Order {private Long id;private Long userId;private Long productId;private Integer count;private BigDecimal money;// 订单状态0 创建中1 已完结private Integer status; } 6.6 创建 Dao 层 创建 dao 层接口 OrderDao里面提供创建订单和更新订单状态的方法代码如下 package com.springcloud.dao;import com.springcloud.entity.Order; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param;Mapper public interface OrderDao {// 创建订单void create(Order order);// 更新订单状态void update(Param(userId) Long userId,Param(status) Integer status); } 在 resources 目录下新建 mapper 文件夹并创建 OrderMapper.xml 文件内容如下 ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.springcloud.dao.OrderDaoresultMap idBaseResultMap typecom.springcloud.entity.Orderid columnid propertyid jdbcTypeBIGINT/result columnuser_id propertyuserId jdbcTypeBIGINT/result columnproduct_id propertyproductId jdbcTypeBIGINT/result columncount propertycount jdbcTypeINTEGER/result columnmoney propertymoney jdbcTypeDECIMAL/result columnstatus propertystatus jdbcTypeINTEGER//resultMapinsert idcreateinsert into t_order (id,user_id,product_id,count,money,status)values (null,#{userId},#{productId},#{count},#{money},0);/insertupdate idupdateupdate t_order set status 1where user_id#{userId} and status #{status};/update/mapper 6.7 创建 service 层 首先创建库存 service 接口用于 openFeign 远程调用代码如下 package com.springcloud.service;import com.springcloud.entity.CommonResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;FeignClient(valueseata-storage-service) public interface StorageService {// 扣减库存PostMapping(value/storage/decrease)CommonResult decrease(RequestParam(productId) Long productId, RequestParam(count) Integer count); }其次创建账户 service 接口用于 openFeign 远程调用代码如下 package com.springcloud.service;import com.springcloud.entity.CommonResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;import java.math.BigDecimal;FeignClient(valueseata-account-service) public interface AccountService {// 扣减余额PostMapping(value/account/decrease)CommonResult decrease(RequestParam(userId) Long userId, RequestParam(money) BigDecimal money); }最后创建订单的 service 接口和实现类代码如下 package com.springcloud.service;import com.springcloud.entity.Order;public interface OrderService {// 创建订单void create(Order order); }package com.springcloud.service.impl;import com.springcloud.dao.OrderDao; import com.springcloud.entity.Order; import com.springcloud.service.AccountService; import com.springcloud.service.OrderService; import com.springcloud.service.StorageService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service;import javax.annotation.Resource;// 下订单-减库存-减余额-改状态 Service Slf4j public class OrderServiceImpl implements OrderService {ResourceOrderDao orderDao;ResourceStorageService storageService;ResourceAccountService accountService;Overridepublic void create(Order order) {log.info(-------下单开始);//1 新建订单orderDao.create(order);//2 扣减库存log.info(-------order-service中扣减库存开始);storageService.decrease(order.getProductId(),order.getCount());log.info(-------order-service中扣减库存结束);//3 扣减账户log.info(-------order-service中扣减余额开始);accountService.decrease(order.getUserId(),order.getMoney());log.info(-------order-service中扣减余额结束);//4 修改订单状态从零到1,1代表已经完成log.info(-------order-service中修改订单状态开始);orderDao.update(order.getUserId(), 0);log.info(-------order-service中修改订单状态结束);log.info(-------下单结束);} }6.8 创建 controller 创建对外提供的 OrderController 类代码如下 package com.springcloud.controller;import com.springcloud.entity.CommonResult; import com.springcloud.entity.Order; import com.springcloud.service.OrderService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;RestController public class OrderController {ResourceOrderService orderService;GetMapping (/order/create)public CommonResult create (Order order){orderService.create(order);return new CommonResult(200,创建成功);}}6.9 创建配置类 接下来创建一个 Mybatis 的配置类用于扫描 mapper 文件代码如下 package com.springcloud.config;import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration;Configuration MapperScan({com.springcloud.dao}) public class MyBatisConfig { } 然后创建一个使用 Seata 对数据源进行代理的配置类代码如下 package com.springcloud.config;import com.alibaba.druid.pool.DruidDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.transaction.SpringManagedTransactionFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;Configuration public class DataSourceProxyConfig {Value(${mybatis.mapperLocations})private String mapperLocations;BeanConfigurationProperties(prefix spring.datasource)public DataSource druidDataSource() {return new DruidDataSource();}Beanpublic DataSourceProxy dataSourceProxy(DataSource dataSource) {return new DataSourceProxy(dataSource);}Beanpublic SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}} 6.10 主启动类 主启动类的代码如下所示 package com.springcloud;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients;EnableDiscoveryClient EnableFeignClients SpringBootApplication(exclude DataSourceAutoConfiguration.class)//取消数据源的自动创建 public class SeataOrderMainApp2001 {public static void main(String[] args){SpringApplication.run(SeataOrderMainApp2001.class, args);} } 6.11 测试 启动模块进行测试可以看到启动没有任何问题如下图 七、库存微服务准备 创建一个 seata-storage-service2002 的库存微服务模块接下来详细的介绍下模块的相关内容。 7.1 添加 maven 依赖 pom.xml 的内容如下所示唯一需要注意的是需要移除 spring-cloud-starter-alibaba-seata 依赖里面包含的 seata 依赖单独引入 seata 依赖版本不兼容。模块之间的调用使用 openfeign。 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns: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/modelVersionparentgroupIdcom.springcloud/groupIdartifactIdSpringCloud/artifactIdversion1.0-SNAPSHOT/version/parentartifactIdseata-storage-service2002/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/propertiesdependencies!--nacos--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency!--seata--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusionartifactIdseata-all/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-all/artifactIdversion0.9.0/version/dependency!--feign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion2.0.0/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.37/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion1.1.10/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency/dependencies /project 7.2 创建 application.yml 在 resources 目录下创建 application.yml 文件内容如下所示唯一需要注意的是自定义事务组名称需要与 seata-server 中的对应即本次的事务组要和我们的 seata 服务器的组要匹配。 server:port: 2002spring:application:name: seata-storage-servicecloud:alibaba:seata:tx-service-group: xhf_tx_groupnacos:discovery:server-addr: localhost:8848datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_storageusername: rootpassword: 123456logging:level:io:seata: infomybatis:mapperLocations: classpath:mapper/*.xml 7.3 创建 file.conf 在 resources 目录下创建 file.conf 文件内容如下所示注意修改 service 和 db 模块的相关信息。这个文件是控制该微服务的我们还有 seata-server 里面还有一个 file.conf 是负责总控的。 transport {# tcp udt unix-domain-sockettype TCP#NIO NATIVEserver NIO#enable heartbeatheartbeat true#thread factory for nettythread-factory {boss-thread-prefix NettyBossworker-thread-prefix NettyServerNIOWorkerserver-executor-thread-prefix NettyServerBizHandlershare-boss-worker falseclient-selector-thread-prefix NettyClientSelectorclient-selector-thread-size 1client-worker-thread-prefix NettyClientWorkerThread# netty boss thread size,will not be used for UDTboss-thread-size 1#auto default pin or 8worker-thread-size 8}shutdown {# when destroy server, wait secondswait 3}serialization seatacompressor none }service {vgroup_mapping.xhf_tx_group default #修改自定义事务组名称default.grouplist 127.0.0.1:8091enableDegrade falsedisable falsemax.commit.retry.timeout -1max.rollback.retry.timeout -1disableGlobalTransaction false }client {async.commit.buffer.limit 10000lock {retry.internal 10retry.times 30}report.retry.count 5tm.commit.retry.count 1tm.rollback.retry.count 1 }## transaction log store store {## store mode: file、dbmode db## file storefile {dir sessionStore# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptionsmax-branch-session-size 16384# globe session size , if exceeded throws exceptionsmax-global-session-size 512# file buffer size , if exceeded allocate new bufferfile-write-buffer-cache-size 16384# when recover batch read sizesession.reload.read_size 100# async, syncflush-disk-mode async}## database storedb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.datasource dbcp## mysql/oracle/h2/oceanbase etc.db-type mysqldriver-class-name com.mysql.jdbc.Driverurl jdbc:mysql://127.0.0.1:3306/seatauser rootpassword 123456min-conn 1max-conn 3global.table global_tablebranch.table branch_tablelock-table lock_tablequery-limit 100} } lock {## the lock store mode: local、remotemode remotelocal {## store locks in users database}remote {## store locks in the seatas server} } recovery {#schedule committing retry period in millisecondscommitting-retry-period 1000#schedule asyn committing retry period in millisecondsasyn-committing-retry-period 1000#schedule rollbacking retry period in millisecondsrollbacking-retry-period 1000#schedule timeout retry period in millisecondstimeout-retry-period 1000 }transaction {undo.data.validation trueundo.log.serialization jacksonundo.log.save.days 7#schedule delete expired undo_log in millisecondsundo.log.delete.period 86400000undo.log.table undo_log }## metrics settings metrics {enabled falseregistry-type compact# multi exporters use comma dividedexporter-list prometheusexporter-prometheus-port 9898 }support {## springspring {# auto proxy the DataSource beandatasource.autoproxy false} } 7.4 创建 registry.conf 在 resources 目录下创建 registry.conf 文件内容如下所示这个配置文件用于标注出我们的这个微服务是向哪个注册中心进行注册的。这个文件是控制该微服务的我们还有 seata-server 里面还有一个 registry.conf 是负责总控的。 registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype nacosnacos {serverAddr localhost:8848namespace cluster default}eureka {serviceUrl http://localhost:8761/eurekaapplication defaultweight 1}redis {serverAddr localhost:6379db 0}zk {cluster defaultserverAddr 127.0.0.1:2181session.timeout 6000connect.timeout 2000}consul {cluster defaultserverAddr 127.0.0.1:8500}etcd3 {cluster defaultserverAddr http://localhost:2379}sofa {serverAddr 127.0.0.1:9603application defaultregion DEFAULT_ZONEdatacenter DefaultDataCentercluster defaultgroup SEATA_GROUPaddressWaitTime 3000}file {name file.conf} }config {# file、nacos 、apollo、zk、consul、etcd3type filenacos {serverAddr localhostnamespace }consul {serverAddr 127.0.0.1:8500}apollo {app.id seata-serverapollo.meta http://192.168.1.204:8801}zk {serverAddr 127.0.0.1:2181session.timeout 6000connect.timeout 2000}etcd3 {serverAddr http://localhost:2379}file {name file.conf} } 7.5 创建实体类 首先创建一个用于返回前端的调用结果的实体类 CommonResult代码如下 package com.springcloud.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;// 用于返回前端的调用结果 Data NoArgsConstructor AllArgsConstructor public class CommonResult T{private Integer code;private String message;private T data;public CommonResult(Integer code,String message){this(code,message,null);} } 然后创建订单的实体类 Storage代码如下 package com.springcloud.entity;import lombok.Data;Data public class Storage {private Long id;/*** 产品id*/private Long productId;/*** 总库存*/private Integer total;/*** 已用库存*/private Integer used;/*** 剩余库存*/private Integer residue; } 7.6 创建 Dao 层 创建 dao 层接口 StorageDao里面提供扣减库存的方法代码如下 package com.springcloud.dao;import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param;Mapper public interface StorageDao {/*** 扣减库存*/void decrease(Param(productId) Long productId, Param(count) Integer count); }在 resources 目录下新建 mapper 文件夹并创建 StorageMapper.xml 文件内容如下 ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.springcloud.dao.StorageDaoresultMap idBaseResultMap typecom.springcloud.entity.Storageid columnid propertyid jdbcTypeBIGINT/result columnproduct_id propertyproductId jdbcTypeBIGINT/result columntotal propertytotal jdbcTypeINTEGER/result columnused propertyused jdbcTypeINTEGER/result columnresidue propertyresidue jdbcTypeINTEGER//resultMapupdate iddecreaseUPDATEt_storageSETused used #{count},residue residue - #{count}WHEREproduct_id #{productId}/update/mapper7.7 创建 service 层 创建库存的 service 接口和实现类代码如下 package com.springcloud.service;public interface StorageService {/*** 扣减库存*/void decrease(Long productId, Integer count); } package com.springcloud.service.impl;import com.springcloud.dao.StorageDao; import com.springcloud.service.StorageService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service;import javax.annotation.Resource;Service Slf4j public class StorageServiceImpl implements StorageService {ResourceStorageDao storageDao;Overridepublic void decrease(Long productId, Integer count) {log.info(-------storage-service中扣减库存开始);storageDao.decrease(productId, count);log.info(-------storage-service中扣减库存结束);} } 7.8 创建 controller 创建对外提供的 StorageController 类代码如下 package com.springcloud.controller;import com.springcloud.entity.CommonResult; import com.springcloud.service.StorageService; import org.apache.ibatis.annotations.Param; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;RestController public class StorageController {ResourceStorageService storageService;/*** 扣减库存*/RequestMapping(/storage/decrease)public CommonResult decrease(RequestParam(productId) Long productId, RequestParam(count) Integer count){storageService.decrease(productId,count);return new CommonResult(200,扣减库存成功);} }7.9 创建配置类 接下来创建一个 Mybatis 的配置类用于扫描 mapper 文件代码如下 package com.springcloud.config;import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration;Configuration MapperScan({com.springcloud.dao}) public class MyBatisConfig { } 然后创建一个使用 Seata 对数据源进行代理的配置类代码如下 package com.springcloud.config;import com.alibaba.druid.pool.DruidDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.transaction.SpringManagedTransactionFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;Configuration public class DataSourceProxyConfig {Value(${mybatis.mapperLocations})private String mapperLocations;BeanConfigurationProperties(prefix spring.datasource)public DataSource druidDataSource() {return new DruidDataSource();}Beanpublic DataSourceProxy dataSourceProxy(DataSource dataSource) {return new DataSourceProxy(dataSource);}Beanpublic SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}} 7.10 主启动类 主启动类的代码如下所示 package com.springcloud;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients;SpringBootApplication(exclude DataSourceAutoConfiguration.class) EnableDiscoveryClient EnableFeignClients public class SeataStorageServiceApplication2002 {public static void main(String[] args) {SpringApplication.run(SeataStorageServiceApplication2002.class, args);}} 7.11 测试 启动模块进行测试可以看到启动没有任何问题如下图 八、账户微服务准备 创建一个 seata-account-service2003 的账户微服务模块接下来详细的介绍下模块的相关内容。 8.1 添加 maven 依赖 pom.xml 的内容如下所示唯一需要注意的是需要移除 spring-cloud-starter-alibaba-seata 依赖里面包含的 seata 依赖单独引入 seata 依赖版本不兼容。模块之间的调用使用 openfeign。 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns: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/modelVersionparentgroupIdcom.springcloud/groupIdartifactIdSpringCloud/artifactIdversion1.0-SNAPSHOT/version/parentartifactIdseata-account-service2003/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/propertiesdependencies!--nacos--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency!--seata--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusionartifactIdseata-all/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-all/artifactIdversion0.9.0/version/dependency!--feign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion2.0.0/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.37/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion1.1.10/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency/dependencies /project 8.2 创建 application.yml 在 resources 目录下创建 application.yml 文件内容如下所示唯一需要注意的是自定义事务组名称需要与 seata-server 中的对应即本次的事务组要和我们的 seata 服务器的组要匹配。 server:port: 2003spring:application:name: seata-account-servicecloud:alibaba:seata:tx-service-group: xhf_tx_groupnacos:discovery:server-addr: localhost:8848datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_accountusername: rootpassword: 123456feign:hystrix:enabled: falselogging:level:io:seata: infomybatis:mapperLocations: classpath:mapper/*.xml 8.3 创建 file.conf 在 resources 目录下创建 file.conf 文件内容如下所示注意修改 service 和 db 模块的相关信息。这个文件是控制该微服务的我们还有 seata-server 里面还有一个 file.conf 是负责总控的。 transport {# tcp udt unix-domain-sockettype TCP#NIO NATIVEserver NIO#enable heartbeatheartbeat true#thread factory for nettythread-factory {boss-thread-prefix NettyBossworker-thread-prefix NettyServerNIOWorkerserver-executor-thread-prefix NettyServerBizHandlershare-boss-worker falseclient-selector-thread-prefix NettyClientSelectorclient-selector-thread-size 1client-worker-thread-prefix NettyClientWorkerThread# netty boss thread size,will not be used for UDTboss-thread-size 1#auto default pin or 8worker-thread-size 8}shutdown {# when destroy server, wait secondswait 3}serialization seatacompressor none }service {vgroup_mapping.xhf_tx_group default #修改自定义事务组名称default.grouplist 127.0.0.1:8091enableDegrade falsedisable falsemax.commit.retry.timeout -1max.rollback.retry.timeout -1disableGlobalTransaction false }client {async.commit.buffer.limit 10000lock {retry.internal 10retry.times 30}report.retry.count 5tm.commit.retry.count 1tm.rollback.retry.count 1 }## transaction log store store {## store mode: file、dbmode db## file storefile {dir sessionStore# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptionsmax-branch-session-size 16384# globe session size , if exceeded throws exceptionsmax-global-session-size 512# file buffer size , if exceeded allocate new bufferfile-write-buffer-cache-size 16384# when recover batch read sizesession.reload.read_size 100# async, syncflush-disk-mode async}## database storedb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.datasource dbcp## mysql/oracle/h2/oceanbase etc.db-type mysqldriver-class-name com.mysql.jdbc.Driverurl jdbc:mysql://127.0.0.1:3306/seatauser rootpassword 123456min-conn 1max-conn 3global.table global_tablebranch.table branch_tablelock-table lock_tablequery-limit 100} } lock {## the lock store mode: local、remotemode remotelocal {## store locks in users database}remote {## store locks in the seatas server} } recovery {#schedule committing retry period in millisecondscommitting-retry-period 1000#schedule asyn committing retry period in millisecondsasyn-committing-retry-period 1000#schedule rollbacking retry period in millisecondsrollbacking-retry-period 1000#schedule timeout retry period in millisecondstimeout-retry-period 1000 }transaction {undo.data.validation trueundo.log.serialization jacksonundo.log.save.days 7#schedule delete expired undo_log in millisecondsundo.log.delete.period 86400000undo.log.table undo_log }## metrics settings metrics {enabled falseregistry-type compact# multi exporters use comma dividedexporter-list prometheusexporter-prometheus-port 9898 }support {## springspring {# auto proxy the DataSource beandatasource.autoproxy false} } 8.4 创建 registry.conf 在 resources 目录下创建 registry.conf 文件内容如下所示这个配置文件用于标注出我们的这个微服务是向哪个注册中心进行注册的。这个文件是控制该微服务的我们还有 seata-server 里面还有一个 registry.conf 是负责总控的。 registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype nacosnacos {serverAddr localhost:8848namespace cluster default}eureka {serviceUrl http://localhost:8761/eurekaapplication defaultweight 1}redis {serverAddr localhost:6379db 0}zk {cluster defaultserverAddr 127.0.0.1:2181session.timeout 6000connect.timeout 2000}consul {cluster defaultserverAddr 127.0.0.1:8500}etcd3 {cluster defaultserverAddr http://localhost:2379}sofa {serverAddr 127.0.0.1:9603application defaultregion DEFAULT_ZONEdatacenter DefaultDataCentercluster defaultgroup SEATA_GROUPaddressWaitTime 3000}file {name file.conf} }config {# file、nacos 、apollo、zk、consul、etcd3type filenacos {serverAddr localhostnamespace }consul {serverAddr 127.0.0.1:8500}apollo {app.id seata-serverapollo.meta http://192.168.1.204:8801}zk {serverAddr 127.0.0.1:2181session.timeout 6000connect.timeout 2000}etcd3 {serverAddr http://localhost:2379}file {name file.conf} } 8.5 创建实体类 首先创建一个用于返回前端的调用结果的实体类 CommonResult代码如下 package com.springcloud.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;// 用于返回前端的调用结果 Data NoArgsConstructor AllArgsConstructor public class CommonResult T{private Integer code;private String message;private T data;public CommonResult(Integer code,String message){this(code,message,null);} } 然后创建账户的实体类 Account代码如下 package com.springcloud.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.math.BigDecimal;Data NoArgsConstructor AllArgsConstructor public class Account {/*** 用户id*/private Long userId;/*** 总额度*/private BigDecimal total;/*** 已用额度*/private BigDecimal used;/*** 剩余额度*/private BigDecimal residue; }8.6 创建 Dao 层 创建 dao 层接口 AccountDao里面提供扣减账户余额的方法代码如下 package com.springcloud.dao;import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param;import java.math.BigDecimal;Mapper public interface AccountDao {/*** 扣减账户余额*/void decrease(Param(userId) Long userId, Param(money) BigDecimal money); }在 resources 目录下新建 mapper 文件夹并创建 AccountMapper.xml 文件内容如下 ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.springcloud.dao.AccountDaoresultMap idBaseResultMap typecom.springcloud.entity.Accountid columnid propertyid jdbcTypeBIGINT/result columnuser_id propertyuserId jdbcTypeBIGINT/result columntotal propertytotal jdbcTypeDECIMAL/result columnused propertyused jdbcTypeDECIMAL/result columnresidue propertyresidue jdbcTypeDECIMAL//resultMapupdate iddecreaseUPDATE t_accountSETresidue residue - #{money},used used #{money}WHEREuser_id #{userId};/update/mapper 8.7 创建 service 层 创建账户的 service 接口和实现类代码如下 package com.springcloud.service;import java.math.BigDecimal;public interface AccountService {void decrease(Long userId,BigDecimal money); }package com.springcloud.service.impl;import com.springcloud.dao.AccountDao; import com.springcloud.service.AccountService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service;import javax.annotation.Resource; import java.math.BigDecimal;Service Slf4j public class AccountServiceImpl implements AccountService {ResourceAccountDao accountDao;Overridepublic void decrease(Long userId, BigDecimal money) {log.info(-------account-service中扣减账户余额开始);accountDao.decrease(userId, money);log.info(-------account-service中扣减账户余额结束);} }8.8 创建 controller 创建对外提供的 AccountController 类代码如下 package com.springcloud.controller;import com.springcloud.entity.CommonResult; import com.springcloud.service.AccountService; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource; import java.math.BigDecimal;RestController public class AccountController {ResourceAccountService accountService;/*** 扣减账户余额*/RequestMapping(/account/decrease)public CommonResult decrease(RequestParam(userId) Long userId, RequestParam(money) BigDecimal money){accountService.decrease(userId,money);return new CommonResult(200,扣减账户余额);} } 8.9 创建配置类 接下来创建一个 Mybatis 的配置类用于扫描 mapper 文件代码如下 package com.springcloud.config;import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration;Configuration MapperScan({com.springcloud.dao}) public class MyBatisConfig { } 然后创建一个使用 Seata 对数据源进行代理的配置类代码如下 package com.springcloud.config;import com.alibaba.druid.pool.DruidDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.transaction.SpringManagedTransactionFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;Configuration public class DataSourceProxyConfig {Value(${mybatis.mapperLocations})private String mapperLocations;BeanConfigurationProperties(prefix spring.datasource)public DataSource druidDataSource() {return new DruidDataSource();}Beanpublic DataSourceProxy dataSourceProxy(DataSource dataSource) {return new DataSourceProxy(dataSource);}Beanpublic SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}} 8.10 主启动类 主启动类的代码如下所示 package com.springcloud;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients;SpringBootApplication(exclude DataSourceAutoConfiguration.class) EnableDiscoveryClient EnableFeignClients public class SeataAccountMainApp2003 {public static void main(String[] args){SpringApplication.run(SeataAccountMainApp2003.class, args);} } 8.11 测试 启动模块进行测试可以看到启动没有任何问题如下图 九、测试 9.1 正常下单 此时数据库的初始数据如下图由于无人下单订单表没有数据库存表一共有 100 个卖了 0 个还剩 100 个账户表一共 1000 元用了 0 元还剩 1000 元。 分别启动三个微服务然后通过浏览器创建订单 http://localhost:2001/order/create?userId1productId1count10money100如下图订单创建成功。 控制台的打印日志输出如下也是没有任何问题的。 此时数据库的数据如下图用户下单成功订单表里面新增了一条数据库存表里面一共 100 个卖出去 10 个还剩 90 个账户表里面一共 1000 元花了 100 元还剩 900 元。 9.2 超时异常 为了演示超时异常的情况我们在 seata-account-service2003 模块的 AccountServiceImpl 类中模拟一个超时的异常因为 openFeign 默认的超时时间为 1s必定会发生异常代码如下 Service Slf4j public class AccountServiceImpl implements AccountService {ResourceAccountDao accountDao;Overridepublic void decrease(Long userId, BigDecimal money) {log.info(-------account-service中扣减账户余额开始);// 模拟超时场景try {Thread.sleep(2000L);} catch (InterruptedException e) {throw new RuntimeException(e);}accountDao.decrease(userId, money);log.info(-------account-service中扣减账户余额结束);} } 此时重启 seata-account-service2003 模块然后再次访问 http://localhost:2001/order/create?userId1productId1count10money100如下图可以看到直接就返回了错误的信息 此时数据库的数据如下图订单表里面新增了一条数据但此条数据处于下单未成功的状态但是库存和金额都扣除了订单状态并没有设置为已经完成没有从 0 改为 1而且由于 feign 的重试机制账户余额还有可能被多次扣减故出现了事务的问题。 9.3 添加全局事务控制 为了解决上面出现的事务问题我们可以在业务类 OrderServiceImpl 上添加一个 GlobalTransactional 注解来解决这个问题代码如下 // 下订单-减库存-减余额-改状态 Service Slf4j public class OrderServiceImpl implements OrderService {ResourceOrderDao orderDao;ResourceStorageService storageService;ResourceAccountService accountService;Override// name叫什么都行rollbackFor发生什么异常时进行回滚GlobalTransactional(name xhf-create-order,rollbackFor Exception.class)public void create(Order order) {log.info(-------下单开始);//1 新建订单orderDao.create(order);//2 扣减库存log.info(-------order-service中扣减库存开始);storageService.decrease(order.getProductId(),order.getCount());log.info(-------order-service中扣减库存结束);//3 扣减账户log.info(-------order-service中扣减余额开始);accountService.decrease(order.getUserId(),order.getMoney());log.info(-------order-service中扣减余额结束);//4 修改订单状态从零到1,1代表已经完成log.info(-------order-service中修改订单状态开始);orderDao.update(order.getUserId(), 0);log.info(-------order-service中修改订单状态结束);log.info(-------下单结束);} }再次重启工程再次访问上述的 url 地址返回结果如下 但是数据库里面三个表的数据没有发生任何的变化这里不再粘图了。 十、一部分补充 10.1 seata 背景 seata 的全称为 Simple Extensible Autonomous Transaction Architecture简单可扩展自治事务框架是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。 从 2020 开始参加工作后用 1.0 以后的版本因为它支持集群了。 10.2 再看三大组件 结合我们开发的案例来讲TC 就是我们单独部署的 seata-serverTM 就是有 GlobalTransactional 注解标注的那个方法而 RM 就是我们编写的订单模块、库存模块和账户模块因为它们都操作了数据库资源。 10.3 分布式事务的执行流程 1、TM 开启分布式事务即 TM 向 TC 注册全局事务记录代码扫描到了 GlobalTransactional 注解 2、按业务场景编排数据库和服务等事务内部资源即 RM 向 TC 汇报资源状态需要让 TC 知道此次事务都涉及到哪些数据库表和资源 3、TM 结束分布式事务事务一阶段结束即 TM 通知 TC 提交/回滚分布式事务。 4、TC 汇总事务信息决定分布式事务是提交还剩回滚。 5、TC 通知所有 RM 提交/回滚资源事务二阶段结束。 10.4 seata 模式 seata 有三种模式供大家选择分别为 AT 模式、TCC 模式和 Saga 模式我们默认使用的都是 AT 模式接下来详细的介绍下这种模式。 10.4.1 seata 模式概述 AT 模式是 Seata 创新的一种非侵入式的分布式事务解决方案Seata 在内部做了对数据库操作的代理层我们使用 Seata AT 模式时实际上用的是 Seata 自带的数据源代理 DataSourceProxySeata 在这层代理中加入了很多逻辑比如插入回滚 undo_log 日志检查全局锁等。 10.4.2 seata 模式整体机制 10.4.3 一阶段加载 在一阶段Seata 会拦截业务 SQL如下图 1 、解析 SQL 语义找到业务 SQL 要更新的业务数据在业务数据被更新前将其保存成 before image。 2、执行业务 SQL 更新业务数据 3、在业务数据更新之后其保存成 after image最后生成行锁。 以上操作全部在一个数据库事务内完成这样保证了一阶段操作的原子性。 10.4.4 二阶段提交 二阶段如是顺利提交的话因为业务 SQL 在一阶段已经提交至数据库所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉完成数据清理即可。如下图 10.4.5 二阶段回滚 二阶段如果是回滚的话Seata 就需要回滚一阶段已经执行的业务 SQL还原业务数据。 回滚方式便是用 before image 还原业务数据但在还原前要首先要校验脏写对比数据库当前业务数据和 after image如果两份数据完全一致就说明没有脏写可以还原业务数据如果不一致就说明有脏写出现脏写就需要转人工处理。如下图
http://www.zqtcl.cn/news/682372/

相关文章:

  • 硚口区建设局网站海绵宝宝的网页设计html源代码
  • 旅游网站建设合同成年做羞羞的视频网站
  • 海门网站建设制作道德建设 网站
  • 苏州 规划建设局网站网页设计师培训费用图
  • 怎么做视频解析的网站QQ空间可以建设网站吗
  • 视频网站 php源码甘肃 网站建设
  • 响应式网站和自适应便宜做网站8818
  • 湖南网站建设mxtia网站建设的流程图
  • 西安网站开发公司电话装修设计网站有哪些
  • 多少钱网站建设个人主页网页设计教程
  • 嘉兴品牌网站建设网站开发项目拖延周期
  • 网站版面布局结构网站建设公司公司
  • 给新公司建网站中国互联网企业排名前十名
  • 中国建设银行网站会员用户名网站建设应列入啥费用
  • 网站上面的水印怎么做的广东网站建设公
  • 爱站网关键词长尾挖掘工具wordpress文章外链
  • 做视频剪辑接私活的网站网站商城系统设计
  • thinkphp5做网站做网站需要准备资料
  • 门户网站平台建设方案建e室内设计网cad
  • 西安网站建设收费标准第五次全国经济普查
  • 成品网站货源1688免费襄阳公司网站建设
  • 2020国内十大小说网站排名365网站
  • 潍坊做网站的网络公司网页设计入门教材pdf
  • 影视公司网站建设wordpress 500ms
  • 旅游网站建设公司crm客户管理系统模板
  • 哪个网站有免费的模板阿里云上如何用iis做网站
  • 中山优化网站门户网站建设jz190
  • 湖州服装网站建设网站备案和域名备案区别
  • 网站开发好学嘛网络安全工程师年薪
  • 17网站一起做网店睡衣网线制作流程