SpringCloud與Seata分布式事務初體驗

在本篇文章中我們在SpringCloud環境下通過使用Seata來模擬用戶購買商品時由于用戶余額不足導致本次訂單提交失敗,來驗證下在MySQL數據庫內事務是否會回滾

本章文章只涉及所需要測試的服務列表以及Seata配置部分。

用戶提交訂單購買商品大致分為以下幾個步驟:

  1. 減少庫存
  2. 扣除金額
  3. 提交訂單

1. 準備環境

  • Seata Server

    如果對Seata Server部署方式還不了解,請訪問:http://blog.yuqiyu.com/seata-init-env.html

  • Eureka Server

    服務注冊中心,如果對Eureka Server部署方式還不了解,請訪問http://blog.yuqiyu.com/eureka-server.html

2. 準備測試服務

為了方便學習的同學查看源碼,我們本章節源碼采用Maven Module(多模塊)的方式進行構建。

我們用于測試的服務所使用的第三方依賴都一致,各個服務的pom.xml文件內容如下所示:

?

<dependencies><!--Web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--openfeign接口定義--><dependency><groupId>org.minbox.chapter</groupId><artifactId>openfeign-service</artifactId><version>0.0.1-SNAPSHOT</version></dependency><!--公共依賴--><dependency><groupId>org.minbox.chapter</groupId><artifactId>common-service</artifactId><version>0.0.1-SNAPSHOT</version></dependency><!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--Eureka Client--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.minbox.framework</groupId><artifactId>api-boot-starter-mybatis-enhance</artifactId></dependency>
</dependencies>

2.1 Openfeign接口定義模塊

由于我們服務之間采用的Openfeign方式進行相互調用,所以創建了一個模塊openfeign-service來提供服務接口的定義

  • 賬戶服務提供的接口定義

賬戶服務對外所提供的Openfeign接口定義如下所示:

?

/*** 賬戶服務接口** @author 恒宇少年*/
@FeignClient(name = "account-service")
@RequestMapping(value = "/account")
public interface AccountClient {/*** 扣除指定賬戶金額** @param accountId 賬戶編號* @param money     金額*/@PostMappingvoid deduction(@RequestParam("accountId") Integer accountId, @RequestParam("money") Double money);
}
  • 商品服務提供的接口定義

    商品服務對外所提供的Openfeign接口定義如下所示:

?

  /*** 商品服務接口定義** @author 恒宇少年*/@FeignClient(name = "good-service")@RequestMapping(value = "/good")public interface GoodClient {/*** 查詢商品基本信息** @param goodId {@link Good#getId()}* @return {@link Good}*/@GetMappingGood findById(@RequestParam("goodId") Integer goodId);/*** 減少商品的庫存** @param goodId {@link Good#getId()}* @param stock  減少庫存的數量*/@PostMappingvoid reduceStock(@RequestParam("goodId") Integer goodId, @RequestParam("stock") int stock);}

2.2 公共模塊

公共模塊common-service內所提供的類是共用的,各個服務都可以調用,其中最為重要的是將Seata所提供的數據源代理(DataSourceProxy)實例化配置放到了這個模塊中,數據庫代理相關配置代碼如下所示:

?

/*** Seata所需數據庫代理配置類** @author 恒宇少年*/
@Configuration
public class DataSourceProxyAutoConfiguration {/*** 數據源屬性配置* {@link DataSourceProperties}*/private DataSourceProperties dataSourceProperties;public DataSourceProxyAutoConfiguration(DataSourceProperties dataSourceProperties) {this.dataSourceProperties = dataSourceProperties;}/*** 配置數據源代理,用于事務回滾** @return The default datasource* @see DataSourceProxy*/@Primary@Bean("dataSource")public DataSource dataSource() {HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl(dataSourceProperties.getUrl());dataSource.setUsername(dataSourceProperties.getUsername());dataSource.setPassword(dataSourceProperties.getPassword());dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());return new DataSourceProxy(dataSource);}
}

該配置類在所需要的服務中使用@Import注解進行導入使用。

2.3 賬戶服務

  • 服務接口實現

    賬戶服務用于提供接口的服務實現,通過實現openfeign-service內提供的AccountClient服務定義接口來對應提供服務實現,實現接口如下所示:

?

  /*** 賬戶接口實現** @author 恒宇少年*/@RestControllerpublic class AccountController implements AccountClient {/*** 賬戶業務邏輯*/@Autowiredprivate AccountService accountService;@Overridepublic void deduction(Integer accountId, Double money) {accountService.deduction(accountId, money);}}
  • 服務配置(application.yml)

?

  # 服務名spring:application:name: account-service# seata分組cloud:alibaba:seata:tx-service-group: minbox-seata# 數據源datasource:url: jdbc:mysql://localhost:3306/testusername: rootpassword: 123456type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driver# eurekaeureka:client:service-url:defaultZone: http://service:nodev2@10.180.98.83:10001/eureka/

通過spring.cloud.alibaba.seata.tx-service-group我們可以指定服務所屬事務的分組,該配置非必填,默認為spring.application.name配置的內容加上字符串-fescar-service-group,如:account-service-fescar-service-group,詳見com.alibaba.cloud.seata.GlobalTransactionAutoConfiguration配置類源碼。

在我本地測試環境的Eureka Server10.180.98.83服務器上,這里需要修改成你們自己的地址,數據庫連接信息也需要修改成你們自己的配置。

  • 導入Seata數據源代理配置

?

  /*** @author 恒宇少年*/@SpringBootApplication@Import(DataSourceProxyAutoConfiguration.class)public class AccountServiceApplication {/*** logger instance*/static Logger logger = LoggerFactory.getLogger(AccountServiceApplication.class);public static void main(String[] args) {SpringApplication.run(AccountServiceApplication.class, args);logger.info("賬戶服務啟動成功.");}}

通過@Import導入我們common-service內提供的Seata數據源代理配置類DataSourceProxyAutoConfiguration

2.4 商品服務

  • 服務接口實現

    商品服務提供商品的查詢以及庫存扣減接口服務,實現openfeign-service提供的GoodClient服務接口定義如下所示:

?

  /*** 商品接口定義實現** @author 恒宇少年*/@RestControllerpublic class GoodController implements GoodClient {/*** 商品業務邏輯*/@Autowiredprivate GoodService goodService;/*** 查詢商品信息** @param goodId {@link Good#getId()}* @return*/@Overridepublic Good findById(Integer goodId) {return goodService.findById(goodId);}/*** 扣減商品庫存** @param goodId {@link Good#getId()}* @param stock  減少庫存的數量*/@Overridepublic void reduceStock(Integer goodId, int stock) {goodService.reduceStock(goodId, stock);}}
  • 服務配置(application.yml)

?

  spring:application:name: good-servicecloud:alibaba:seata:tx-service-group: minbox-seatadatasource:url: jdbc:mysql://localhost:3306/testusername: rootpassword: 123456type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Drivereureka:client:service-url:defaultZone: http://service:nodev2@10.180.98.83:10001/eureka/server:port: 8081
  • 導入Seata數據源代理配置

?

  /*** @author 恒宇少年*/@SpringBootApplication@Import(DataSourceProxyAutoConfiguration.class)public class GoodServiceApplication {/*** logger instance*/static Logger logger = LoggerFactory.getLogger(GoodServiceApplication.class);public static void main(String[] args) {SpringApplication.run(GoodServiceApplication.class, args);logger.info("商品服務啟動成功.");}}

2.5 訂單服務

  • 服務接口

    訂單服務提供了下單的接口,通過調用該接口完成下單功能,下單接口會通過Openfeign調用account-servicegood-service所提供的服務接口來完成數據驗證,如下所示:

?

  /*** @author 恒宇少年*/@RestController@RequestMapping(value = "/order")public class OrderController {/*** 賬戶服務接口*/@Autowiredprivate AccountClient accountClient;/*** 商品服務接口*/@Autowiredprivate GoodClient goodClient;/*** 訂單業務邏輯*/@Autowiredprivate OrderService orderService;/*** 通過{@link GoodClient#reduceStock(Integer, int)}方法減少商品的庫存,判斷庫存剩余數量* 通過{@link AccountClient#deduction(Integer, Double)}方法扣除商品所需要的金額,金額不足由account-service拋出異常** @param goodId    {@link Good#getId()}* @param accountId {@link Account#getId()}* @param buyCount  購買數量* @return*/@PostMapping@GlobalTransactionalpublic String submitOrder(@RequestParam("goodId") Integer goodId,@RequestParam("accountId") Integer accountId,@RequestParam("buyCount") int buyCount) {Good good = goodClient.findById(goodId);Double orderPrice = buyCount * good.getPrice();goodClient.reduceStock(goodId, buyCount);accountClient.deduction(accountId, orderPrice);Order order = toOrder(goodId, accountId, orderPrice);orderService.addOrder(order);return "下單成功.";}private Order toOrder(Integer goodId, Integer accountId, Double orderPrice) {Order order = new Order();order.setGoodId(goodId);order.setAccountId(accountId);order.setPrice(orderPrice);return order;}}
  • 服務配置(application.yml)

?

  spring:application:name: order-servicecloud:alibaba:seata:tx-service-group: minbox-seatadatasource:url: jdbc:mysql://localhost:3306/testusername: rootpassword: 123456type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Drivereureka:client:service-url:defaultZone: http://service:nodev2@10.180.98.83:10001/eureka/server:port: 8082
  • 啟用Openfeign & 導入Seata數據源代理配置

?

  /*** @author 恒宇少年*/@SpringBootApplication@EnableFeignClients(basePackages = "org.minbox.chapter.seata.openfeign")@Import(DataSourceProxyAutoConfiguration.class)public class OrderServiceApplication {/*** logger instance*/static Logger logger = LoggerFactory.getLogger(OrderServiceApplication.class);public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);logger.info("訂單服務啟動成功.");}}

我們僅在order-service調用了其他服務的Openfeign接口,所以我們只需要在order-service內通過@EnableFeignClients注解啟用Openfeign接口實現代理。

3. 服務連接Seata Server

服務想要連接到Seata Server需要添加兩個配置文件,分別是registry.conffile.conf

  • registry.conf

    注冊到Seata Server的配置文件,里面包含了注冊方式、配置文件讀取方式,內容如下所示:

?

  registry {# file、nacos、eureka、redis、zk、consultype = "file"file {name = "file.conf"}}config {type = "file"file {name = "file.conf"}}
  • file.conf

    該配置文件內包含了使用file方式連接到Eureka Server的配置信息以及存儲分布式事務信息的方式,如下所示:

?

  transport {# tcp udt unix-domain-sockettype = "TCP"#NIO NATIVEserver = "NIO"#enable heartbeatheartbeat = true#thread factory for nettythread-factory {boss-thread-prefix = "NettyBoss"worker-thread-prefix = "NettyServerNIOWorker"server-executor-thread-prefix = "NettyServerBizHandler"share-boss-worker = falseclient-selector-thread-prefix = "NettyClientSelector"client-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}}## transaction log storestore {## store mode: file、dbmode = "file"## 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 {datasource = "druid"db-type = "mysql"driver-class-name = "com.mysql.jdbc.Driver"url = "jdbc:mysql://10.180.98.83:3306/iot-transactional"user = "dev"password = "dev2019."}}service {vgroup_mapping.minbox-seata = "default"default.grouplist = "10.180.98.83:8091"enableDegrade = falsedisable = false}client {async.commit.buffer.limit = 10000lock {retry.internal = 10retry.times = 30}}

配置文件內service部分需要注意,我們在application.yml配置文件內配置了事務分組為minbox-seata,在這里需要進行對應配置vgroup_mapping.minbox-seata = "default",通過default.grouplist = "10.180.98.83:8091"配置Seata Server的服務列表。

將上面兩個配置文件在各個服務resources目錄下創建。

4. 編寫下單邏輯

在前面說了那么多,只是做了準備工作,我們要為每個參與下單的服務添加對應的業務邏輯。

  • 賬戶服務

    account-service內添加賬戶余額扣除業務邏輯類,AccountService如下所示:

?

  /*** 賬戶業務邏輯處理** @author 恒宇少年*/@Service@Transactional(rollbackFor = Exception.class)public class AccountService {@Autowiredprivate EnhanceMapper<Account, Integer> mapper;/*** {@link EnhanceMapper} 具體使用查看ApiBoot官網文檔http://apiboot.minbox.io/zh-cn/docs/api-boot-mybatis-enhance.html** @param accountId {@link Account#getId()}* @param money     扣除的金額*/public void deduction(Integer accountId, Double money) {Account account = mapper.selectOne(accountId);if (ObjectUtils.isEmpty(account)) {throw new RuntimeException("賬戶:" + accountId + ",不存在.");}if (account.getMoney() - money < 0) {throw new RuntimeException("賬戶:" + accountId + ",余額不足.");}account.setMoney(account.getMoney().doubleValue() - money);mapper.update(account);}}
  • 商品服務

    good-service內添加查詢商品、扣減商品庫存的邏輯類,GoodService如下所示:

?

  /*** 商品業務邏輯實現** @author 恒宇少年*/@Service@Transactional(rollbackFor = Exception.class)public class GoodService {@Autowiredprivate EnhanceMapper<Good, Integer> mapper;/*** 查詢商品詳情** @param goodId {@link Good#getId()}* @return {@link Good}*/public Good findById(Integer goodId) {return mapper.selectOne(goodId);}/*** {@link EnhanceMapper} 具體使用查看ApiBoot官網文檔http://apiboot.minbox.io/zh-cn/docs/api-boot-mybatis-enhance.html* 扣除商品庫存** @param goodId {@link Good#getId()}* @param stock  扣除的庫存數量*/public void reduceStock(Integer goodId, int stock) {Good good = mapper.selectOne(goodId);if (ObjectUtils.isEmpty(good)) {throw new RuntimeException("商品:" + goodId + ",不存在.");}if (good.getStock() - stock < 0) {throw new RuntimeException("商品:" + goodId + "庫存不足.");}good.setStock(good.getStock() - stock);mapper.update(good);}}

5. 提交訂單測試

我們在執行測試之前在數據庫內的seata_accountseata_good表內對應添加兩條測試數據,如下所示:

?

-- seata_good
INSERT INTO `seata_good` VALUES (1,'華為Meta 30',10,5000.00); -- seata_account
INSERT INTO `seata_account` VALUES (1,10000.00,'2019-10-11 02:37:35',NULL);

增加SEATA恢復表

DROP SCHEMA IF EXISTS zeroa;CREATE SCHEMA zeroa;USE zeroa;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,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;DROP SCHEMA IF EXISTS zerob;CREATE SCHEMA zerob;USE zerob;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,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

5.1 啟動服務

將我們本章所使用good-serverorder-serviceaccount-service三個服務啟動。

5.2 測試點:正常購買

我們添加的賬戶余額測試數據夠我們購買兩件商品,我們先來購買一件商品驗證下接口訪問是否成功,通過如下命令訪問下單接口:

?

~ curl -X POST http://localhost:8082/order\?goodId\=1\&accountId\=1\&buyCount\=1
下單成功.

通過我們訪問/order下單接口,根據響應的內容我們確定商品已經購買成功。

通過查看order-service控制臺內容:

?

2019-10-11 16:52:15.477  INFO 13142 --- [nio-8082-exec-4] i.seata.tm.api.DefaultGlobalTransaction  : [10.180.98.83:8091:2024417333] commit status:Committed
2019-10-11 16:52:16.412  INFO 13142 --- [atch_RMROLE_2_8] i.s.core.rpc.netty.RmMessageListener     : onMessage:xid=10.180.98.83:8091:2024417333,branchId=2024417341,branchType=AT,resourceId=jdbc:mysql://localhost:3306/test,applicationData=null
2019-10-11 16:52:16.412  INFO 13142 --- [atch_RMROLE_2_8] io.seata.rm.AbstractRMHandler            : Branch committing: 10.180.98.83:8091:2024417333 2024417341 jdbc:mysql://localhost:3306/test null
2019-10-11 16:52:16.412  INFO 13142 --- [atch_RMROLE_2_8] io.seata.rm.AbstractRMHandler            : Branch commit result: PhaseTwo_Committed

我們可以看到本次事務已經成功Committed

再去驗證下數據庫內的賬戶余額商品庫存是否有所扣減。

5.3 測試點:庫存不足

測試商品添加了10個庫存,在之前測試已經銷售掉了一件商品,我們測試購買數量超過庫存數量時,是否有回滾日志,執行如下命令:

?

~ curl -X POST http://localhost:8082/order\?goodId\=1\&accountId\=1\&buyCount\=10
{"timestamp":"2019-10-11T08:57:13.775+0000","status":500,"error":"Internal Server Error","message":"status 500 reading GoodClient#reduceStock(Integer,int)","path":"/order"}

在我們good-service服務控制臺已經打印了商品庫存不足的異常信息:

?

java.lang.RuntimeException: 商品:1庫存不足.at org.minbox.chapter.seata.service.GoodService.reduceStock(GoodService.java:42) ~[classes/:na]....

我們再看order-service的控制臺打印日志:

?

Begin new global transaction [10.180.98.83:8091:2024417350]
2019-10-11 16:57:13.771  INFO 13142 --- [nio-8082-exec-5] i.seata.tm.api.DefaultGlobalTransaction  : [10.180.98.83:8091:2024417350] rollback status:Rollbacked

通過日志可以查看本次事務進行了回滾

由于庫存的驗證在賬戶余額扣減之前,所以我們本次并不能從數據庫的數據來判斷事務是真的回滾。

5.4 測試點:余額不足

既然商品庫存不足我們不能直接驗證數據庫事務回滾,我們從賬戶余額不足來下手,在之前成功購買了一件商品,賬戶的余額還夠購買一件商品,商品庫存目前是9件,我們本次測試購買5件商品,這樣就會出現購買商品庫存充足余額不足的應用場景,執行如下命令發起請求:

?

~ curl -X POST http://localhost:8082/order\?goodId\=1\&accountId\=1\&buyCount\=5
{"timestamp":"2019-10-11T09:03:00.794+0000","status":500,"error":"Internal Server Error","message":"status 500 reading AccountClient#deduction(Integer,Double)","path":"/order"}

我們通過查看account-service控制臺日志可以看到:

?

java.lang.RuntimeException: 賬戶:1,余額不足.at org.minbox.chapter.seata.service.AccountService.deduction(AccountService.java:33) ~[classes/:na]

已經拋出了余額不足的異常。

通過查看good-serviceorder-serivce控制臺日志,可以看到事務進行了回滾操作。

接下來查看seata_account表數據,我們發現賬戶余額沒有改變,賬戶服務的事務回滾驗證成功

查看seata_good表數據,我們發現商品的庫存也沒有改變,商品服務的事務回滾驗證成功

6. 總結

本章主要來驗證分布式事務框架SeataMySQL下提交與回滾有效性,是否能夠完成我們預期的效果,Seata作為SpringCloud Alibaba的核心框架,更新頻率比較高,快速的解決使用過程中遇到的問題,是一個潛力股,不錯的選擇。

由于本章設計的代碼比較多,請結合源碼進行學習。

7. 本章源碼

請訪問<a href="https://gitee.com/hengboy/spring-cloud-chapter" target="_blank">https://gitee.com/hengboy/spring-cloud-chapter</a>查看本章源碼,建議使用git clone https://gitee.com/hengboy/spring-cloud-chapter.git將源碼下載到本地。

  • Gitee:https://gitee.com/hengboy/spring-boot-chapter



?

坑點一
如果你的項目采用是spring-cloud-alibaba-seata 0.9.0版本或以下的話,它集成了fescar-spring的0.4.2版本,如果你的seata-server服務端是采用0.5.0以上建議還是降低版本,采用0.4.2版本。因為0.4.2版本解壓是fescar-server名字,意不意外。這就是坑。而且項目引入seata依賴會與舊版本的fescar依賴沖突。

? ? ? ? ? ? <dependency>
? ? ? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>
? ? ? ? ? ? ? ? <artifactId>spring-cloud-alibaba-seata</artifactId>
? ? ? ? ? ? ? ? <version>0.9.0.RELEASE</version>
? ? ? ? ? ? </dependency>
?


如果你的項目采用是spring-cloud-alibaba-seata 0.9.1(這個的seata為0.5.2)版本以上的話,那恭喜你。你可以使用seata-server的0.5.2以上的版本了。只需要在依賴這樣引入

? ? ? ?<dependency>
? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>
? ? ? ? ? ? <artifactId>spring-cloud-alibaba-seata</artifactId>
? ? ? ? ? ? <version>0.9.1.BUILD-SNAPSHOT</version>
? ? ? ? ? ? <exclusions>
? ? ? ? ? ? ? ? <exclusion>
? ? ? ? ? ? ? ? ? ? <groupId>io.seata</groupId>
? ? ? ? ? ? ? ? ? ? <artifactId>seata-spring</artifactId>
? ? ? ? ? ? ? ? </exclusion>
? ? ? ? ? ? </exclusions>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>io.seata</groupId>
? ? ? ? ? ? <artifactId>seata-all</artifactId>
? ? ? ? ? ? <version>seata-server對應的版本</version>
? ? ? ? </dependency>
?


坑點二
需要在每個服務中的resources文件中添加兩個文件file.conf和registry.conf


具體以seata-server中的file.conf和registry.conf為準。

坑點三
如果你的項目采用是spring-cloud-alibaba-seata 0.9.0版本或以下的話,因為它集成了fescar-spring的0.4.2版本,如果你是使用nacos來配置參數的話,建議使用seata-server 0.4.2,不然引入seata0.5.0以上的版本依賴會混淆,容易報以下錯誤

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'globalTransactionScanner' defined in class path resource [org/springframework/cloud/alibaba/seata/GlobalTransactionAutoConfiguration.class]: Invocation of init method failed; nested exception is java.lang.ExceptionInInitializerError
Caused by: java.lang.ExceptionInInitializerError: null
Caused by: java.lang.NullPointerException: Name is null
?? ?at java.lang.Enum.valueOf(Enum.java:236) ~[na:1.8.0_201]
?? ?at com.alibaba.fescar.core.rpc.netty.TransportProtocolType.valueOf(TransportProtocolType.java:25) ~[fescar-core-0.4.2.jar:na]
?? ?at com.alibaba.fescar.core.rpc.netty.NettyBaseConfig.<clinit>(NettyBaseConfig.java:114) ~[fescar-core-0.4.2.jar:na]
?? ?... 23 common frames omitted
?


坑點四
數據源需要引入druid依賴

/**
?* @author lgt
?*/
@Configuration
public class DatabaseConfiguration {

?? ?@Bean
?? ?@ConfigurationProperties(prefix = "spring.datasource")
?? ?public DruidDataSource druidDataSource() {
?? ??? ?return new DruidDataSource();
?? ?}

?? ?/**
?? ? * 需要將 DataSourceProxy 設置為主數據源,否則事務無法回滾
?? ? *
?? ? * @param druidDataSource The DruidDataSource
?? ? * @return The default datasource
?? ? */
?? ?@Primary
?? ?@Bean("dataSource")
?? ?public DataSource dataSource(DruidDataSource druidDataSource) {
?? ??? ?return new DataSourceProxy(druidDataSource);
?? ?}

}

ps:上面是 seata 數據源的配置,數據源采用 druid 的DruidDataSource,但實際 jdbcTemplate 執行時并不是用該數據源,而用的是 seata 對DruidDataSource的代理DataSourceProxy,所以,與 RM 相關的代碼邏輯基本上都是從DataSourceProxy這個代理數據源開始的。

坑點五
0.6.1及之前版本的啟動命令是:sh seata-server.sh 8091 file 127.0.0.1
0.7.0 及之后版本的啟動命令是:sh seata-server.sh -p 8091 -h 127.0.0.1 -m file
0.5.2及之前的版本的數據庫和之后版本數據庫是不一樣,詳細以github的文件一致
————————————————
版權聲明:本文為CSDN博主「sbit_」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/sbit_/article/details/96112393

作者:恒宇少年
鏈接:https://www.jianshu.com/p/0a92b7c97c65
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386607.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386607.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386607.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

想學IT的必看!今年Android面試必問的這些技術面,架構師必備技能

第一次觀看我文章的朋友&#xff0c;可以關注、點贊、轉發一下&#xff0c;每天分享各種干貨技術和程序猿趣事 前言 職場的金三銀四跳槽季又來了&#xff0c;不同的是今年比往年「冷」一些&#xff0c;形式更加嚴峻一些&#xff0c;大家多多少少可能都聽到或看到一些信息&…

springboot集成redis使用redis作為session報錯ClassNotFoundException類RememberMeServices

springboot 集成redis使用redis作為緩存&#xff0c;會報錯的問題。 錯誤信息&#xff1a; java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration.taskSchedulerat org.springframew…

阿里巴巴分布式事務利器Seata環境準備

阿里巴巴自從跟SpringCloud共同發起創建微服務開源社區時&#xff0c;開啟了SpringCloud Alibaba分支&#xff0c;而且在生態內提供了一款適用于分布式應用程序&#xff08;Dubbo、SpringCloud等&#xff09;的事務框架Seata&#xff0c;該框架經過多個大版本的發布&#xff0c…

對于‘敲什么都隊’自主開發的《校園服務》軟件的使用體驗

信1805-1 邊信哲 20183694 在六月十三日我系組織的2017級軟件工程交流大會中&#xff0c;我為第十一組敲什么都隊’自主開發的《校園服務》軟件投出了我的一票&#xff0c;在為數眾多的校園服務類軟件中&#xff0c;《校園服務》最吸引我的地方就是他們的軟件能完成數據…

阿里P7大牛親自教你!BAT這種大廠履歷意味著什么?積累總結

金九銀十過后各大網絡平臺都是各種面經分享&#xff0c;包括已收offer&#xff0c;或面試失敗的都有&#xff0c;相信大部分人都拿到了自己心儀的大廠offer&#xff0c;不過也有沒有少數沒能進到自己內心向往的大廠而懊惱的&#xff0c;那么到底如何才能進大廠&#xff0c;該準…

啟動mac版docker自帶的k8s

最近準備好好學習下k8s&#xff0c;為了圖方便&#xff0c;直接使用docker集成的k8s&#xff0c;但是網上找了一些教程但都沒能一次性成功&#xff0c;只好自己從頭跑一遍&#xff0c;順手寫個教程可以方便有類似需求的同學參考。 話不多說&#xff0c;直接上步驟。 1.下載doc…

yum安裝mysql

在CentOS7中默認安裝有MariaDB&#xff0c;這個是MySQL的分支&#xff0c;但為了需要&#xff0c;還是要在系統中安裝MySQL&#xff0c;而且安裝完成之后可以直接覆蓋掉MariaDB。 1. 下載并安裝MySQL官方的 Yum Repository 1[rootBrianZhu /]# wget -i -c http://dev.mysql.com…

springboot很多以來jar包是在外部當時候,如何打dockerfile到阿里云

首先保證springboot與各種jar包文件夾在同一目錄 dockerfile如下內容 FROM frolvlad/alpine-oraclejdk8 VOLUME /usr/cloud ADD lib /lib/ ADD lib_attachment /lib_attachment/ ADD lib_bigdata /lib_bigdata/ ADD lib_bpm /lib_bpm/ ADD lib_deploy /lib_deploy/ ADD lib_el…

阿里P7大牛手把手教你!一眼就能看懂的Android自學手冊,真香!

前言 曾聽過很多人說Android學習很簡單&#xff0c;做個App就上手了&#xff0c;工作機會多&#xff0c;畢業后也比較容易找工作。這種觀點可能是很多Android開發者最開始入行的原因之一。 在工作初期&#xff0c;工作主要是按照業務需求實現App頁面的功能&#xff0c;按照設…

【VScode】使用VScode 來寫markdown時序圖

準備工作在VScode中下載插件Markdown Preview Enhanced插件創建一個.md文件在VScode中打開文件&#xff0c;界面內點擊右鍵可以看到Open preview to the side(還有很多方法外面都能搜到)&#xff0c;可以進行實時預覽效果開始markdown第一行主標題&#xff08;次標題依次加#&am…

阿里P7大牛整理!BAT大廠面試基礎題集合,成功入職字節跳動

都說大廠面試必問源碼&#xff0c;可很多人看完MMKV 源碼、Handler 源碼、Binder 源碼、OkHttp 源碼等源碼記不住&#xff0c;是腦子有問題嗎&#xff1f;當然不是&#xff01;是因為你沒有掌握學習源碼的技巧。 我的朋友子路&#xff0c;很多人都叫他路神&#xff0c;稱他為“…

大項目之網上書城(八)——數據庫大改添加圖書

目錄 大項目之網上書城&#xff08;八&#xff09;——數據庫大改&添加圖書主要改動1.數據庫新增表代碼2.數據庫新增觸發器3.其他對BookService和BookDao的修改代碼4.addBook.jsp代碼效果圖5.AddNewBookServlet代碼大項目之網上書城&#xff08;八&#xff09;——數據庫大…

hping3工具DOS攻擊實驗

需要兩臺機器&#xff0c;一臺扮演攻擊源&#xff0c;另一做目標源。 攻擊源地址:10.0.40.4 被攻擊機器地址:10.0.40.246 2 被攻擊的機器上安裝tcpdump&#xff0c;tcpdump主要是用來抓包的&#xff0c;看看網絡數據包是否到達。 $ yum install tcpdump -y 3 首先開啟tcp…

騰訊T2親自講解!搞懂開源框架設計思想真的這么重要嗎?系列篇

Java相關 無論什么級別的Android從業者&#xff0c;Java作為Android開發基礎語言。不管是工作還是面試中&#xff0c;Java都是必考題。如果不懂Java的話&#xff0c;薪酬會非常吃虧&#xff08;美團尤為重視Java基礎&#xff09; 詳細介紹了Java泛型、注解、并發編程、數據傳…

解決Docker容器內訪問宿主機MySQL數據庫服務器的問題

懶得描述太多,總歸是解決了問題,方法簡要記錄如下,雖然簡要,但是完整,一來紀念處理該問題耗費的大半天時間,二來本著共享精神幫助其他遇到該問題的哥們兒,當然這個方法并不一定能解決你們的問題,但是多少能提供些解決思路. 第一,先檢查防火墻,通常應該沒什么問題 (問題解決之后…

阿里P7親自教你!我的頭條面試經歷分享,完整PDF

前言 轉眼間&#xff0c;2020 年已過去一大半了&#xff0c;2020 年很難&#xff0c;各企業裁員的消息蠻多的&#xff0c;降職&#xff0c;不發年終獎等等。2020 年確實是艱難的一年。然而生活總是要繼續&#xff0c;時間不給你喪的機會&#xff01;如果我們能堅持下來&#x…

Java多線程 ——線程基礎和鎖鎖鎖

Java多線程(一) 一、線程的定義二、Synchronize線程同步三、偏向鎖、自旋鎖、重量級鎖四、volatile關鍵字 4.1.普通變量運算的物理意義4.2.有無解決的方案4.3.volatile的幾個特性&#xff08;參考https://www.cnblogs.com/kubidemanong/p/9505944.html&#xff09;五、Compare …

阿里P7級別面試經驗總結,進階學習資料!

一、前言 本人面試已經很久之前了&#xff0c;分享一下我做美團面試官的經歷吧。 美團上海面試&#xff0c;2-1及以下美團是不社招的&#xff0c;校招和2-2~2-3社招一般是三面&#xff0c;格外優秀3-1及以上會加簽面試。初面技術基礎&#xff0c;二面業務感知和技術項目&#…

C 預處理指令

0. Overview C的預處理指令格式為#name&#xff0c;均以#開頭&#xff0c;#和指令名之間不可有空白字符&#xff0c;#前可以有空字符&#xff0c;但為增強可讀性&#xff0c;一般應從第一列開始 #name不能由宏展開得來&#xff0c;name也不能由宏展開得來&#xff0c;如 // Wro…

Windows NAT端口映射

Windows本身命令行支持配置端口映射&#xff0c;條件是已經安裝了IPV6&#xff0c;啟不啟用都無所謂&#xff0c;我在win7和server2008上是可以的。xp&#xff0c;2003裝了ipv6協議也是可以的。 CMD下操作 增加端口映射&#xff0c;將10.10.10.10的8080映射到10.10.10.11的80…