1、導入相關依賴
<!-- shardingsphere-jdbc --><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc</artifactId><version>5.5.1</version></dependency><!-- shardingsphere 集成 seata AT 模式 --><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-transaction-base-seata-at</artifactId><version>5.5.1</version></dependency><dependency><groupId>org.apache.seata</groupId><artifactId>seata-all</artifactId><version>2.3.0</version><exclusions><exclusion><groupId>org.antlr</groupId><artifactId>antlr4-runtime</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><exclusions><exclusion><groupId>com.netflix.archaius</groupId><artifactId>archaius-core</artifactId></exclusion></exclusions></dependency>
備注:
【1】shardingsphere-transaction-base-seata-at 的版本最好與 shardingsphere-jdbc 版本一致
【2】seata-all 版本最好與 seata-server 服務端版本一致
2、在應用服務的 classpath 目錄下創建以下配置文件
【1】sharding-config.yml:用于支持讀寫分離。這里以應用已經集成了 shardingsphere-jdbc 為前提,可參考:
https://blog.csdn.net/hkl_Forever/article/details/146602740
【2】seata.conf:用于 shardingsphere-jdbc 支持 seata 分布式事務,內容案例如下:
shardingsphere.transaction.seata.at.enable = true
shardingsphere.transaction.seata.tx.timeout = 120client {application.id = order-servicetransaction.service.group = default_tx_group
}service {vgroupMapping.default_tx_group = "default"default.grouplist = "服務IP:8091"
}
備注:
(1)事務組名稱可以自定義,但要與seata服務端配置文件(seata-server.yml)中配置的事務組名稱一致(否則報錯),
(2)注意?default_tx_group、default?的映射關系要對應
【3】registry.conf:用于訪問 seata 服務所在的注冊中心和配置中心,內容案例如下:
registry {type = "nacos"nacos {serverAddr = "http://nacos服務ip:8850"username = "xxx"password = "xxx"namespace = "xxx"group = "DEFAULT_GROUP"}
}config {type = "nacos"nacos {serverAddr = "http://nacos服務ip:8850"username = "xxx"password = "xxx"namespace = "xxx"group = "DEFAULT_GROUP"dataId = "seata-server.yml" # Seata服務端的配置文件,一定要正確保證能訪問到該文件}
}
【4】在集成 seata 服務對應的數據庫中創建 undo_log 表,(此為必須,否則全局事務無法回滾),可參考:
https://blog.csdn.net/hkl_Forever/article/details/145803842
3、傳遞 TX_XID(全局事務id)
【1】在調用方的服務中配置openfeign的接口 RequestInterceptor 的實現類進行傳遞
@Configuration
public class TransferTxXidInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {//---------------------- 傳遞 seata 的 TX_XID start ---------------------String xid = RootContext.getXID();if (StrUtil.isNotBlank(xid)) {requestTemplate.header(RootContext.KEY_XID, xid);}//---------------------- 傳遞 seata 的 TX_XID end ---------------------}}
【2】在被調用方的服務中配置 seata 支持的事務傳播攔截器,獲取調用方傳遞過來的 TX_XID
@Configuration
public class JakartaSeataConfig {/*** <p>微服務事務傳播攔截器(適用SpringBoot 3.x)</p>*/@Beanpublic JakartaSeataWebMvcConfigurer getJakartaSeataWebMvcConfigurer() {return new JakartaSeataWebMvcConfigurer();}// /**
// * <p>微服務事務傳播攔截器(適用SpringBoot 2.x)</p>
// */
// @Bean
// public SeataWebMvcConfigurer getSeataWebMvcConfigurer() {
// return new SeataWebMvcConfigurer();
// }}
4、驗證測試,啟用微服務應用
調用方代碼案例
@Transactional(rollbackFor = Exception.class)@Overridepublic void saveOrder(AddOrderReq data) {if (ObjUtil.isNull(data)) {return;}//保存訂單Order order = BeanUtil.copyProperties(data, Order.class);order.setOrderNo("S-" + IdUtil.getSnowflake().nextIdStr());order.setOrderTotalPrice(ObjUtil.defaultIfNull(data.getOrderTotalPrice(), NumberUtil.toBigDecimal(0.00)));this.save(order);//記錄支付流水InsPaymentFlowReq insPaymentFlowReq = new InsPaymentFlowReq();insPaymentFlowReq.setOrderNo(order.getOrderNo());insPaymentFlowReq.setCostPrice(order.getOrderTotalPrice());insPaymentFlowReq.setRemark(order.getRemark());paymentFlowClient.insPaymentFlow(insPaymentFlowReq);ThrowUtil.fail("order服務出錯了!");}
被調用方代碼案例
@Transactional(rollbackFor = {Exception.class})@Overridepublic void insPaymentFlow(InsPaymentFlowReq data) {if (ObjUtil.isNull(data)) {return;}PaymentFlow paymentFlow = BeanUtil.copyProperties(data, PaymentFlow.class);paymentFlow.setFlowNo("F-" + IdUtil.getSnowflake().nextIdStr());this.save(paymentFlow);//ThrowUtil.fail("payment服務出錯了!");}
經測試驗證后,在 shardingsphere-jdbc 讀寫分離的前提下,服務調用鏈路中有報錯雙方都可以正常回滾,符合預期
5、總結注意
【1】shardingsphere-jdbc 集成 seata 與 單數據源集成 seata 完全是各自獨立的方式(不搭嘎)
【2】shardingsphere-jdbc 集成 seata 后,切記在主方法上要使用 @Transactional 不能使用 @GlobalTransactional。單數據源集成 seata 則在主方法上使用 @GlobalTransactional 即可
【3】shardingsphere-jdbc 集成 seata 后,如果只使用讀寫分離場景沒問題。但如果使用分片、分庫分表場景則 seata 事務不靠譜(不建議分片場景和seata一起使用)