您好,登錄后才能下訂單哦!
Seata
是 阿里巴巴開源的分布式事務中間件,以高效并且對業務0侵入的方式,解決微服務場景下面臨的分布式事務問題。
事實上,官方在GitHub
已經給出了多種環境下的Seata
應用示例項目,地址:https://github.com/seata/seata-samples
。
為什么筆者要重新寫一遍呢,主要原因有兩點:
官網代碼示例中,依賴太多,分不清哪些有什么作用
Seata相關資料較少,博主在搭建的過程中,遇到了一些坑,記錄一下
本文涉及軟件環境如下:
SpringBoot 2.1.6.RELEASE
Dubbo 2.7.1
Mybatis 3.5.1
Seata 0.6.1
Zookeeper 3.4.10
為了簡化流程,我們只需要訂單和庫存兩個服務。創建訂單的時候,調用庫存服務,扣減庫存。
涉及的表設計如下:
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;
打開https://github.com/seata/seata/releases
,目前最新版本是v0.6.1
。
下載解壓后,到seata-server-0.6.1\distribution\bin
目錄下可以看到seata-server.bat和seata-server.sh
,選擇一個雙擊執行。
不出意外的話,當你看到-Server started ...
等字樣,就正常啟動了。
由于是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
等就不粘了,自行引入即可。
這里只需要配置數據庫連接信息和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
Seata 是通過代理數據源實現事務分支,所以需要先配置一個數據源的代理,否則事務不會回滾。
@Bean?public?DataSourceProxy?dataSourceProxy(DataSource?dataSource)?{?return?new?DataSourceProxy(dataSource);?}
注意,這里的DataSourceProxy
類位于io.seata.rm.datasource
包內。
還需要配置全局事務掃描器。有兩個參數,一個是應用名稱,一個是事務分組。
@Bean?public?GlobalTransactionScanner?globalTransactionScanner()?{?return?new?GlobalTransactionScanner("springboot-order",?"my_test_tx_group");?}
事實上,關于Seata事務的一系列初始化工作都在這里完成。
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?}
在庫存服務中,拿到商品編碼和購買總個數,扣減即可。
<update?id="decreaseStorage">?update?t_storage?set?count?=?count-${count}?where?commodity_code?=?#{commodityCode}?</update>
然后用Dubbo將庫存服務扣減接口暴露出去。
在訂單服務中,先扣減庫存,再創建訂單。最后拋出異常,然后去數據庫檢查事務是否回滾。
@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進行傳播。
請切記,Seata 是通過代理數據源實現事務分支,一定不要忘記配置數據源代理。
在數據庫中,表里的主鍵ID字段都是自增的。如果你的字段不是自增的,那么在Mybatis的insert SQL
中,要將列名寫完整。
比如我們可以這樣寫SQL:
INSERT?INTO?table_name?VALUES?(值1,?值2,....)
那么這時候就要寫成:
INSERT?INTO?table_name?(列1,?列2,...)?VALUES?(值1,?值2,....)
在訂單表中,amount
字段類型為double
。在seata0.6.1
版本中,默認的序列化方式為fastjson
,但它會將這個字段序列化成bigdecimal
類型,會導致后面類型不匹配。
但是在后續的seata0.7.0
版本中(還未發布),已經將默認的序列化方式改為了jackson
。
不過無需擔心,這個問題一般不會出現。博主是因為引錯了一個包,才導致發現這問題。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。