您好,登錄后才能下訂單哦!
前言
Seata 是 阿里巴巴開源的分布式事務中間件,以高效并且對業務0侵入的方式,解決微服務場景下面臨的分布式事務問題。
事實上,官方在GitHub已經給出了多種環境下的Seata應用示例項目,地址:https://github.com/seata/seata-samples。
為什么筆者要重新寫一遍呢,主要原因有兩點:
一、環境準備
本文涉及軟件環境如下:
1、業務場景
為了簡化流程,我們只需要訂單和庫存兩個服務。創建訂單的時候,調用庫存服務,扣減庫存。
涉及的表設計如下:
CREATE TABLE `t_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_no` varchar(255) DEFAULT NULL, `user_id` varchar(255) DEFAULT NULL, `commodity_code` varchar(255) DEFAULT NULL, `count` int(11) DEFAULT '0', `amount` double(14,2) DEFAULT '0.00', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8; CREATE TABLE `t_storage` ( `id` int(11) NOT NULL AUTO_INCREMENT, `commodity_code` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `count` int(11) DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `commodity_code` (`commodity_code`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 另外還需要一個回滾日志表: CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) 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, `context` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8;
2、Seata下載安裝
打開https://github.com/seata/seata/releases,目前最新版本是v0.6.1。
下載解壓后,到seata-server-0.6.1\distribution\bin目錄下可以看到seata-server.bat和seata-server.sh,選擇一個雙擊執行。
不出意外的話,當你看到-Server started ...等字樣,就正常啟動了。
3、Maven依賴
由于是Dubbo項目,我們先引入Dubbo相關依賴。
<dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>2.7.1</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.1</version> </dependency> Dubbo的服務要注冊到Zookeeper,引入curator客戶端。 <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.13.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.13.0</version> </dependency>
最后,引入Seata。
<dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>0.6.1</version> </dependency>
當然了,還有其他的如Mybatis、mysql-connector等就不粘了,自行引入即可。
二、項目配置
1、application.properties
這里只需要配置數據庫連接信息和Dubbo相關信息即可。
server.port=8011 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seata spring.datasource.username=root spring.datasource.password=root dubbo.application.name=order-service dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.protocol.name=dubbo dubbo.protocol.port=20881 dubbo.consumer.timeout=9999999 dubbo.consumer.check=false
2、數據源
Seata 是通過代理數據源實現事務分支,所以需要先配置一個數據源的代理,否則事務不會回滾。
@Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); }
注意,這里的DataSourceProxy類位于io.seata.rm.datasource包內。
3、Seata配置
還需要配置全局事務掃描器。有兩個參數,一個是應用名稱,一個是事務分組。
@Bean public GlobalTransactionScanner globalTransactionScanner() { return new GlobalTransactionScanner("springboot-order", "my_test_tx_group"); }
事實上,關于Seata事務的一系列初始化工作都在這里完成。
4、配置注冊中心
Seata連接到服務器的時候需要一些配置項,這時候有一個registry.conf文件可以指定注冊中心和配置文件是什么。
這里有很多可選性,比如file、nacos 、apollo、zk、consul。
后面4個都是業界成熟的配置注冊中心產品,為啥還有個file呢?
官方的初衷是在不依賴第三方配置注冊中心的基礎上快速集成測試seata功能,但是file類型本身不具備注冊中心的動態發現和動態配置功能。
registry.conf文件內容如下:
registry { type = "file" file { name = "file.conf" } } config { # file、nacos 、apollo、zk、consul type = "file" file { name = "file.conf" } }
如果你選擇了file類型,通過name屬性指定了file.conf,這個文件中指定了客戶端或服務器的配置信息。比如傳輸協議、服務器地址等。
service { #vgroup->rgroup vgroup_mapping.my_test_tx_group = "default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support enableDegrade = false #disable disable = false }
三、業務代碼
1、庫存服務
在庫存服務中,拿到商品編碼和購買總個數,扣減即可。
<update id="decreaseStorage"> update t_storage set count = count-${count} where commodity_code = #{commodityCode} </update>
然后用Dubbo將庫存服務扣減接口暴露出去。
2、訂單服務
在訂單服務中,先扣減庫存,再創建訂單。最后拋出異常,然后去數據庫檢查事務是否回滾。
@GlobalTransactional public void createOrder(OrderDTO orderDTO) { System.out.println("開始全局事務。XID="+RootContext.getXID()); StorageDTO storageDTO = new StorageDTO(); storageDTO.setCount(orderDTO.getCount()); storageDTO.setCommodityCode(orderDTO.getCommodityCode()); //1、扣減庫存 storageDubboService.decreaseStorage(storageDTO); //2、創建訂單 orderDTO.setId(order_id.incrementAndGet()); orderDTO.setOrderNo(UUID.randomUUID().toString()); Order order = new Order(); BeanUtils.copyProperties(orderDTO,order); orderMapper.createOrder(order); throw new RuntimeException("分布式事務異常..."+orderDTO.getOrderNo()); }
值得注意的是,在訂單服務事務開始的方法上,需要標注@GlobalTransactional。另外,在庫存服務的方法里,不需要此注解,事務會通過Dubbo進行傳播。
四、注意事項
1、數據源
請切記,Seata 是通過代理數據源實現事務分支,一定不要忘記配置數據源代理。
2、主鍵自增
在數據庫中,表里的主鍵ID字段都是自增的。如果你的字段不是自增的,那么在Mybatis的insert SQL中,要將列名寫完整。
比如我們可以這樣寫SQL:
INSERT INTO table_name VALUES (值1, 值2,....)
那么這時候就要寫成:
INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)
3、序列化問題
在訂單表中,amount字段類型為double。在seata0.6.1版本中,默認的序列化方式為fastjson,但它會將這個字段序列化成bigdecimal類型,會導致后面類型不匹配。
但是在后續的seata0.7.0版本中(還未發布),已經將默認的序列化方式改為了jackson。
不過無需擔心,這個問題一般不會出現。筆者是因為引錯了一個包,才導致發現這問題。
4、本文代碼
本文示例代碼在:https://github.com/taoxun/springboot-dubbo-zookeeper-seata。
5、其他
歡迎有問題及時交流~
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。