在
Spring Boot
單體服務中,新增@Transactional
註解就能實現事務。在單體服務中,執行事務都是在同一個資料庫下進行。但是隨著業務越來越複雜,資料量越來越大會進行分庫分表。在微服務場景下,每個服務都有自己的資料庫。之前的單體事務無法處理跨庫的事務,這個時候就需要使用分散式事務。
前面 Seata 環境搭建 介紹了seata
的安裝,安裝後就需要結合實戰專案介紹分散式事務的應用。
spring-cloud-starter-alibaba:2.1.1.RELEASE
spring-cloud-alibaba-dependencies:2.1.1.RELEASE
Seata Server 1.5.2
Nacos Server:2.0.1
不同的版本實現可能不太相同,儘量保持版本一致。
使用者下單的業務邏輯,整個邏輯有兩個微服務提供支援:
使用者下單購買商品。
有三個專案,倉庫服務stock
、訂單服務order
、seata
服務。seata
服務先呼叫訂單服務,然後呼叫倉庫服務。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Cloud Alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>$Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
三個專案的依賴都是一樣的。
seata
需要分別設定設定中心
和註冊中心
。
設定中心
內部放置著各種組態檔,你可以通過自己所需進行獲取設定載入到對應的使用者端.比如Seata Client端(TM,RM),Seata Server(TC),會去讀取全域性事務開關,事務對談儲存模式等資訊。
註冊中心
可以說是微服務架構中的」通訊錄「,它記錄了服務和服務地址的對映關係。在分散式架構中,服務會註冊到這裡,當服務需要呼叫其它服務時,就到這裡找到服務的地址,進行呼叫.比如Seata Client端(TM,RM),發現Seata Server(TC)叢集的地址,彼此通訊.
從官網檔案找到Nacos 設定中心,在application.yml
加入對應的設定:
seata:
config:
type: nacos
nacos:
server-addr: xxxx
group: SEATA_GROUP
namespace: xxxxxx
username: nacos
password: nacos
group
、namespace
在Nacos
控制中心頁面可以找到,分別對應下圖:
Nacos註冊中心同樣可以獲取如下設定:
seata:
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group : SEATA_GROUP
namespace: xxxxx
username: nacos
password: nacos
application
對應的是Seata Server
設定的服務名。Seata Server
將自己封裝成一個微服務註冊到Nacos
註冊中心。其他設定和設定中心設定一致。
全部貼程式碼,文章也比較繁瑣。所以使用介面的方式,簡單宣告程式碼即可。
倉庫扣減庫存:
public interface StockService {
/**
* 減庫存
* @param id 商品id
* @param num 扣減數量
*/
void reduceStock(Long id, BigDecimal num);
}
建立訂單:
public interface OrderService {
/**
* 建立訂單
* @param num 下單數量
* @param price 商品價格
* @param goodsId 商品id
*/
Order create(BigDecimal num,BigDecimal price,Long goodsId);
}
先建立訂單,然後扣減庫存
@GlobalTransactional(rollbackFor = Exception.class)
public void order(Long goodsId) {
orderService.create(BigDecimal.TEN,BigDecimal.TEN,goodsId);
stockService.reduceStock(goodsId,BigDecimal.TEN);
}
呼叫下單服務,庫存不夠,報錯了,但是建立訂單不會回滾。
事務沒有回滾,從官網範例下載到本地執行,卻可以回滾,對比兩者控制檯輸出之後,發現官網範例使用了mybatis-plus
自動新增了DataSourceProxy
資料來源代理。
所以需要設定seata
代理資料來源。
設定Druid
資料來源代理:
@Configuration
public class DataSourceProxyConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:/mapper/*.xml"));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}
}
同時需要關閉資料來源自動代理:
seata.enable-auto-data-source-proxy: false
再呼叫下單介面,倉庫報錯報錯,建立訂單回滾。訂單服務控制檯也提示了資料回滾:
2023-03-01 23:49:32.162 WARN 66509 --- [nio-8040-exec-3] c.a.c.seata.web.SeataHandlerInterceptor : xid in change during RPC from 192.168.31.115:8091:1864853653168447501 to null
2023-03-01 23:49:33.653 INFO 66509 --- [h_RMROLE_1_3_24] i.s.c.r.p.c.RmBranchRollbackProcessor : rm handle branch rollback process:xid=192.168.31.115:8091:1864853653168447501,branchId=1864853653168447502,branchType=AT,resourceId=jdbc:mysql://34.80.215.211:3306/test,applicationData=null
2023-03-01 23:49:33.653 INFO 66509 --- [h_RMROLE_1_3_24] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.31.115:8091:1864853653168447501 1864853653168447502 jdbc:mysql://34.80.215.211:3306/test
2023-03-01 23:49:36.005 INFO 66509 --- [h_RMROLE_1_3_24] i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.31.115:8091:1864853653168447501 branch 1864853653168447502, undo_log deleted with GlobalFinished
2023-03-01 23:49:36.393 INFO 66509 --- [h_RMROLE_1_3_24] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
PhaseTwo_Rollbacked
表示兩階段回滾。
控制檯報錯:
can not get cluster name in registry config 'service.vgroupMapping.nacos-provide-order-seata-service-group', please make sure registry config correct
can not get cluster name in registry config 'service.vgroupMapping.nacos-provide-order-seata-service-group', please make sure registry config correct
can not get cluster name in registry config 'service.vgroupMapping.nacos-provide-order-seata-service-group', please make sure registry config correct
服務在Nacos
設定中心找不到service.vgroupMapping.nacos-provide-order-seata-service-group
,這是在找分組事務,官網檔案有如下介紹:
設定中心有service.vgroupMapping.default_tx_group
組態檔,根據上圖講解,需要在application.yml
設定:
seata:
tx-service-group: default_tx_group
設定後還是報錯。定位到報錯的原始碼,向上調式原始碼,在application.yml
設定:
spring:
cloud:
alibaba:
seata:
tx-service-group: default_tx_group
本文介紹了Spring Cloud
整合分散式事務seta
,主要有:
application.yml
設定,主要新增nacos
設定中心和註冊中心的設定。搭建服務完成之後,事務不回滾,對比官網範例專案。需要新增資料來源代理,同時關閉據源自動代理。分散式事務就生效了。
控制檯一直報錯can not get cluster name in registry config
,通過偵錯原始碼,找到問題的根源,這裡學到了通過原始碼解決問題。以前前段時間一直在看原始碼,這次通過原始碼解決問題,努力還是會有收穫的。