SpringBoot+ Dubbo + Mybatis + Nacos +Seata整合來實(shí)現(xiàn)Dubbo分布式事務(wù)

作者:請(qǐng)叫我東子
https://blog.csdn.net/u010046908/article/details/100536439
1.簡介
“本文主要介紹SpringBoot2.1.5 + Dubbo 2.7.3 + Mybatis 3.4.2 + Nacos 1.1.3 +Seata 0.8.0整合來實(shí)現(xiàn)Dubbo分布式事務(wù)管理,使用Nacos 作為 Dubbo和Seata的注冊(cè)中心和配置中心,使用 MySQL 數(shù)據(jù)庫和 MyBatis來操作數(shù)據(jù)。
”如果你還對(duì)
SpringBoot、Dubbo、Nacos、Seata、Mybatis不是很了解的話,這里我為大家整理個(gè)它們的官網(wǎng)網(wǎng)站,如下
SpringBoot:https://spring.io/projects/spring-boot Dubbo:http://dubbo.apache.org/en-us/ Nacos:https://nacos.io/zh-cn/docs/quick-start.html Seata:https://github.com/seata/seata/wiki/Home_Chinese MyBatis:http://www.mybatis.org/mybatis-3/zh/index.html 在這里我們就不一個(gè)一個(gè)介紹它們是怎么使用和原理,詳細(xì)請(qǐng)學(xué)習(xí)官方文檔,在這里我將開始對(duì)它們進(jìn)行整合,完成一個(gè)簡單的案例,來讓大家了解
Seata來實(shí)現(xiàn)Dubbo分布式事務(wù)管理的基本流程。2.環(huán)境準(zhǔn)備
2.1 下載nacos并安裝啟動(dòng)
nacos下載:https://github.com/alibaba/nacos/releases/tag/1.1.3
Nacos 快速入門:https://nacos.io/en-us/docs/quick-start.html
sh?startup.sh?-m?standalone
1在瀏覽器打開Nacos web 控制臺(tái):http://192.168.10.200:8848/nacos/index.html
輸入nacos的賬號(hào)和密碼 分別為nacos:nacos
這是時(shí)候naocs 就正常啟動(dòng)了。
2.2 下載seata0.8.0 并安裝啟動(dòng)
2.2.1 在 Seata Release 下載最新版的 Seata Server 并解壓得到如下目錄:
.
├──bin
├──conf
├──file_store
└──lib
123452.2.2 修改 conf/registry.conf 配置,
目前seata支持如下的file、nacos 、apollo、zk、consul的注冊(cè)中心和配置中心。這里我們以
nacos為例。將 type 改為 nacosregistry?{
??#?file?nacos
??type?=?"nacos"
??nacos?{
????serverAddr?=?"192.168.10.200:8848"
????namespace?=?"public"
????cluster?=?"default"
??}
??file?{
????name?=?"file.conf"
??}
}
config?{
??#?file、nacos?、apollo、zk、consul
??type?=?"nacos"
??nacos?{
????serverAddr?=?"192.168.10.200:8848"
????namespace?=?"public"
????cluster?=?"default"
??}
??file?{
????name?=?"file.conf"
??}
}
1234567891011121314151617181920212223242526272829
serverAddr = “192.168.10.200:8848” :nacos 的地址 namespace = “public” :nacos的命名空間默認(rèn)為 publiccluster = “default” :集群設(shè)置未默認(rèn) default注意:seata0.9.0之后,配置如下, 其中
namespace = ""registry?{
??#?file?nacos
??type?=?"nacos"
??nacos?{
????serverAddr?=?"192.168.10.200:8848"
????namespace?=?""
????cluster?=?"default"
??}
??file?{
????name?=?"file.conf"
??}
}
config?{
??#?file、nacos?、apollo、zk、consul
??type?=?"nacos"
??nacos?{
????serverAddr?=?"192.168.10.200:8848"
????namespace?=?""
??}
??file?{
????name?=?"file.conf"
??}
}
1234567891011121314151617181920212223242526272.2.3 修改 conf/nacos-config.txt配置
注意:如果你的seata是1.1之后的版本請(qǐng)按照如下導(dǎo)入方式操作
下載https://github.com/seata/seata/blob/develop/script/config-center/目錄下的 config.txt文件放入到seata-server目錄下的conf中,可更改文件名為nacos-config.txt。下載https://github.com/seata/seata/tree/develop/script/config-center/nacos/目錄下的nacos-config.py文件(window可用python命令執(zhí)行,linux也可執(zhí)行,前提先有安裝python)或者nacos-config.sh文件(window下可放在git命令行執(zhí)行,linux可執(zhí)行文件) transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.thread-factory.boss-thread-prefix=NettyBoss
transport.thread-factory.worker-thread-prefix=NettyServerNIOWorker
transport.thread-factory.server-executor-thread-prefix=NettyServerBizHandler
transport.thread-factory.share-boss-worker=false
transport.thread-factory.client-selector-thread-prefix=NettyClientSelector
transport.thread-factory.client-selector-thread-size=1
transport.thread-factory.client-worker-thread-prefix=NettyClientWorkerThread
transport.thread-factory.boss-thread-size=1
transport.thread-factory.worker-thread-size=8
transport.shutdown.wait=3
service.vgroup_mapping.order-service-seata-service-group=default
service.vgroup_mapping.account-service-seata-service-group=default
service.vgroup_mapping.storage-service-seata-service-group=default
service.vgroup_mapping.business-service-seata-service-group=default
service.enableDegrade=false
service.disable=false
service.max.commit.retry.timeout=-1
service.max.rollback.retry.timeout=-1
client.async.commit.buffer.limit=10000
client.lock.retry.internal=10
client.lock.retry.times=30
store.mode=db
store.file.dir=file_store/data
store.file.max-branch-session-size=16384
store.file.max-global-session-size=512
store.file.file-write-buffer-cache-size=16384
store.file.flush-disk-mode=async
store.file.session.reload.read_size=100
store.db.driver-class-name=com.mysql.jdbc.Driver
store.db.datasource=dbcp
store.db.db-type=mysql
store.db.url=jdbc:mysql://192.168.10.200:3306/seata?useUnicode=true
store.db.user=lidong
store.db.password=cwj887766@@
store.db.min-conn=1
store.db.max-conn=3
store.db.global.table=global_table
store.db.branch.table=branch_table
store.db.query-limit=100
store.db.lock-table=lock_table
recovery.committing-retry-period=1000
recovery.asyn-committing-retry-period=1000
recovery.rollbacking-retry-period=1000
recovery.timeout-retry-period=1000
transaction.undo.data.validation=true
transaction.undo.log.serialization=jackson
transaction.undo.log.save.days=7
transaction.undo.log.delete.period=86400000
transaction.undo.log.table=undo_log
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registry-type=compact
metrics.exporter-list=prometheus
metrics.exporter-prometheus-port=9898
service.default.grouplist=127.0.0.1:8091
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859這里主要修改了如下幾項(xiàng):
store.mode :存儲(chǔ)模式 默認(rèn)file 這里我修改為db 模式 ,并且需要三個(gè)表 global_table、branch_table和lock_tablestore.db.driver-class-name:默認(rèn)沒有,會(huì)報(bào)錯(cuò)。添加了 com.mysql.jdbc.Driverstore.db.datasource=dbcp :數(shù)據(jù)源 dbcp store.db.db-type=mysql : 存儲(chǔ)數(shù)據(jù)庫的類型為 mysqlstore.db.url=jdbc:mysql://192.168.10.200:3306/seata?useUnicode=true : 修改為自己的數(shù)據(jù)庫 url、port、數(shù)據(jù)庫名稱store.db.user=lidong :數(shù)據(jù)庫的賬號(hào) store.db.password=cwj887766@@ :數(shù)據(jù)庫的密碼 service.vgroup_mapping.order-service-seata-service-group=default service.vgroup_mapping.account-service-seata-service-group=default service.vgroup_mapping.storage-service-seata-service-group=default service.vgroup_mapping.business-service-seata-service-group=default 也可以在 Nacos 配置頁面添加,data-id 為 service.vgroup_mapping.${YOUR_SERVICE_NAME}-seata-service-group, group 為 SEATA_GROUP, 如果不添加該配置,啟動(dòng)后會(huì)提示no available server to connect to services-server
注意: 配置文件末尾有空行,需要?jiǎng)h除,否則會(huì)提示失敗,盡管實(shí)際上是成功的
db模式下的所需的三個(gè)表的數(shù)據(jù)庫腳本位于
seata\conf\db_store.sql
global_table的表結(jié)構(gòu)CREATE?TABLE?`global_table`?(
??`xid`?varchar(128)?NOT?NULL,
??`transaction_id`?bigint(20)?DEFAULT?NULL,
??`status`?tinyint(4)?NOT?NULL,
??`application_id`?varchar(64)?DEFAULT?NULL,
??`transaction_service_group`?varchar(64)?DEFAULT?NULL,
??`transaction_name`?varchar(64)?DEFAULT?NULL,
??`timeout`?int(11)?DEFAULT?NULL,
??`begin_time`?bigint(20)?DEFAULT?NULL,
??`application_data`?varchar(2000)?DEFAULT?NULL,
??`gmt_create`?datetime?DEFAULT?NULL,
??`gmt_modified`?datetime?DEFAULT?NULL,
??PRIMARY?KEY?(`xid`),
??KEY?`idx_gmt_modified_status`?(`gmt_modified`,`status`),
??KEY?`idx_transaction_id`?(`transaction_id`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8mb4;
1234567891011121314151617
branch_table的表結(jié)構(gòu)CREATE?TABLE?`branch_table`?(
??`branch_id`?bigint(20)?NOT?NULL,
??`xid`?varchar(128)?NOT?NULL,
??`transaction_id`?bigint(20)?DEFAULT?NULL,
??`resource_group_id`?varchar(32)?DEFAULT?NULL,
??`resource_id`?varchar(256)?DEFAULT?NULL,
??`lock_key`?varchar(128)?DEFAULT?NULL,
??`branch_type`?varchar(8)?DEFAULT?NULL,
??`status`?tinyint(4)?DEFAULT?NULL,
??`client_id`?varchar(64)?DEFAULT?NULL,
??`application_data`?varchar(2000)?DEFAULT?NULL,
??`gmt_create`?datetime?DEFAULT?NULL,
??`gmt_modified`?datetime?DEFAULT?NULL,
??PRIMARY?KEY?(`branch_id`),
??KEY?`idx_xid`?(`xid`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8mb4;
123456789101112131415161718
lock_table的表結(jié)構(gòu)create?table?`lock_table`?(
??`row_key`?varchar(128)?not?null,
??`xid`?varchar(96),
??`transaction_id`?long?,
??`branch_id`?long,
??`resource_id`?varchar(256)?,
??`table_name`?varchar(32)?,
??`pk`?varchar(32)?,
??`gmt_create`?datetime?,
??`gmt_modified`?datetime,
??primary?key(`row_key`)
);
1234567891011122.2.4 將 Seata 配置添加到 Nacos 中
使用命令如下
cd?conf
sh?nacos-config.sh?localhost
12成功后會(huì)提示
init?nacos?config?finished,?please?start?seata-server
1在 Nacos 管理頁面應(yīng)該可以看到有 59 個(gè) Group 為SEATA_GROUP的配置
2.2.5 啟動(dòng) Seata Server
使用db 模式啟動(dòng)
?cd?..
?sh?./bin/seata-server.sh
12這時(shí)候在 Nacos 的服務(wù)列表下面可以看到一個(gè)名為serverAddr的服務(wù)
3 案例分析
參考官網(wǎng)中用戶購買商品的業(yè)務(wù)邏輯。整個(gè)業(yè)務(wù)邏輯由4個(gè)微服務(wù)提供支持:
庫存服務(wù):扣除給定商品的存儲(chǔ)數(shù)量。 訂單服務(wù):根據(jù)購買請(qǐng)求創(chuàng)建訂單。 帳戶服務(wù):借記用戶帳戶的余額。 業(yè)務(wù)服務(wù):處理業(yè)務(wù)邏輯。 請(qǐng)求邏輯架構(gòu)
3.1 github地址
springboot-dubbo-seata:https://github.com/lidong1665/springboot-dubbo-seata
samples-common :公共模塊 samples-account :用戶賬號(hào)模塊 samples-order :訂單模塊 samples-storage :庫存模塊 samples-business :業(yè)務(wù)模塊 3.2 賬戶服務(wù):AccountDubboService
/**
?*?@Author:?lidong
?*?@Description??賬戶服務(wù)接口
?*?@Date?Created?in?2019/9/5?16:37
?*/
public?interface?AccountDubboService?{
????/**
?????*?從賬戶扣錢
?????*/
????ObjectResponse?decreaseAccount(AccountDTO?accountDTO);
}
1234567891011123.3 訂單服務(wù):OrderDubboService
/**
?*?@Author:?lidong
?*?@Description??訂單服務(wù)接口
?*?@Date?Created?in?2019/9/5?16:28
?*/
public?interface?OrderDubboService?{
????/**
?????*?創(chuàng)建訂單
?????*/
????ObjectResponse?createOrder(OrderDTO?orderDTO) ;
}
1234567891011123.4 庫存服務(wù):StorageDubboService
/**
?*?@Author:?lidong
?*?@Description??庫存服務(wù)
?*?@Date?Created?in?2019/9/5?16:22
?*/
public?interface?StorageDubboService?{
????/**
?????*?扣減庫存
?????*/
????ObjectResponse?decreaseStorage(CommodityDTO?commodityDTO);
}
123456789101112133.5 業(yè)務(wù)服務(wù):BusinessService
/**
?*?@Author:?lidong
?*?@Description
?*?@Date?Created?in?2019/9/5?17:17
?*/
public?interface?BusinessService?{
????/**
?????*?出處理業(yè)務(wù)服務(wù)
??????*?@param?businessDTO
?????*?@return
?????*/
????ObjectResponse?handleBusiness(BusinessDTO?businessDTO);
}
123456789101112131415業(yè)務(wù)邏輯的具體實(shí)現(xiàn)主要體現(xiàn)在 訂單服務(wù)的實(shí)現(xiàn)和業(yè)務(wù)服務(wù)的實(shí)現(xiàn)
訂單服務(wù)的實(shí)現(xiàn)
@Service
public?class?TOrderServiceImpl?extends?ServiceImpl<TOrderMapper,?TOrder>?implements?ITOrderService?{
????@Reference(version?=?"1.0.0")
????private?AccountDubboService?accountDubboService;
????/**
?????*?創(chuàng)建訂單
?????*?@Param:??OrderDTO??訂單對(duì)象
?????*?@Return:??OrderDTO??訂單對(duì)象
?????*/
????@Override
????public?ObjectResponse?createOrder(OrderDTO?orderDTO)? {
????????ObjectResponse?response?=?new?ObjectResponse<>();
????????//扣減用戶賬戶
????????AccountDTO?accountDTO?=?new?AccountDTO();
????????accountDTO.setUserId(orderDTO.getUserId());
????????accountDTO.setAmount(orderDTO.getOrderAmount());
????????ObjectResponse?objectResponse?=?accountDubboService.decreaseAccount(accountDTO);
????????//生成訂單號(hào)
????????orderDTO.setOrderNo(UUID.randomUUID().toString().replace("-",""));
????????//生成訂單
????????TOrder?tOrder?=?new?TOrder();
????????BeanUtils.copyProperties(orderDTO,tOrder);
????????tOrder.setCount(orderDTO.getOrderCount());
????????tOrder.setAmount(orderDTO.getOrderAmount().doubleValue());
????????try?{
????????????baseMapper.createOrder(tOrder);
????????}?catch?(Exception?e)?{
????????????response.setStatus(RspStatusEnum.FAIL.getCode());
????????????response.setMessage(RspStatusEnum.FAIL.getMessage());
????????????return?response;
????????}
????????if?(objectResponse.getStatus()?!=?200)?{
????????????response.setStatus(RspStatusEnum.FAIL.getCode());
????????????response.setMessage(RspStatusEnum.FAIL.getMessage());
????????????return?response;
????????}
????????response.setStatus(RspStatusEnum.SUCCESS.getCode());
????????response.setMessage(RspStatusEnum.SUCCESS.getMessage());
????????return?response;
????}
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546整個(gè)業(yè)務(wù)的實(shí)現(xiàn)邏輯
@Service
@Slf4j
public?class?BusinessServiceImpl?implements?BusinessService{
????@Reference(version?=?"1.0.0")
????private?StorageDubboService?storageDubboService;
????@Reference(version?=?"1.0.0")
????private?OrderDubboService?orderDubboService;
????private?boolean?flag;
????/**
?????*?處理業(yè)務(wù)邏輯
?????*?@Param:
?????*?@Return:
?????*/
????@GlobalTransactional(timeoutMills?=?300000,?name?=?"dubbo-gts-seata-example")
????@Override
????public?ObjectResponse?handleBusiness(BusinessDTO?businessDTO)?{
????????log.info("開始全局事務(wù),XID?=?"?+?RootContext.getXID());
????????ObjectResponse3.6 使用seata的分布式事務(wù)解決方案處理dubbo的分布式事務(wù)
我們只需要在業(yè)務(wù)處理的方法
handleBusiness添加一個(gè)注解@GlobalTransactional@GlobalTransactional(timeoutMills?=?300000,?name?=?"dubbo-gts-seata-example")
????@Override
????public?ObjectResponse?handleBusiness(BusinessDTO?businessDTO)?{
????
????}
12345
timeoutMills: 超時(shí)時(shí)間name:事務(wù)名稱3.7 準(zhǔn)備數(shù)據(jù)庫
注意: MySQL必須使用
InnoDB engine.創(chuàng)建數(shù)據(jù)庫 并導(dǎo)入數(shù)據(jù)庫腳本
DROP?DATABASE?IF?EXISTS?seata;
CREATE?DATABASE?seata;
USE?seata;
DROP?TABLE?IF?EXISTS?`t_account`;
CREATE?TABLE?`t_account`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`user_id`?varchar(255)?DEFAULT?NULL,
??`amount`?double(14,2)?DEFAULT?'0.00',
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=2?DEFAULT?CHARSET=utf8;
--?----------------------------
--?Records?of?t_account
--?----------------------------
INSERT?INTO?`t_account`?VALUES?('1',?'1',?'4000.00');
--?----------------------------
--?Table?structure?for?t_order
--?----------------------------
DROP?TABLE?IF?EXISTS?`t_order`;
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=64?DEFAULT?CHARSET=utf8;
--?----------------------------
--?Records?of?t_order
--?----------------------------
--?----------------------------
--?Table?structure?for?t_storage
--?----------------------------
DROP?TABLE?IF?EXISTS?`t_storage`;
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;
--?----------------------------
--?Records?of?t_storage
--?----------------------------
INSERT?INTO?`t_storage`?VALUES?('1',?'C201901140001',?'水杯',?'1000');
--?----------------------------
--?Table?structure?for?undo_log
--?注意此處0.3.0+?增加唯一索引?ux_undo_log
--?----------------------------
DROP?TABLE?IF?EXISTS?`undo_log`;
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;
--?----------------------------
--?Records?of?undo_log
--?----------------------------
SET?FOREIGN_KEY_CHECKS=1;
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576會(huì)看到如上的4個(gè)表
+-------------------------+
|?Tables_in_seata?????????|
+-------------------------+
|?t_account???????????????|
|?t_order?????????????????|
|?t_storage???????????????|
|?undo_log????????????????|
+-------------------------+
12345678這里為了簡化我將這個(gè)三張表創(chuàng)建到一個(gè)庫中,使用是三個(gè)數(shù)據(jù)源來實(shí)現(xiàn)。
3.8 我們以賬號(hào)服務(wù)
samples-account為例 ,分析需要注意的配置項(xiàng)目3.8.1 引入的依賴
"1.0"?encoding="UTF-8"?>"http://maven.apache.org/POM/4.0.0"?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
?????????xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/xsd/maven-4.0.0.xsd">
????4.0.0
????
????????org.springframework.boot
????????spring-boot-starter-parent
????????2.1.5.RELEASE
?????????
????
????springboot-dubbo-seata
????pom
????springboot-dubbo-seata
????io.seata
????1.0.0
????samples-account
????
????????2.1.5.RELEASE
????????1.8
????????1.1.10
????????1.3.2
????????2.3
????????0.2.3
????????1.16.22
????????2.7.3
????????1.1.3
????????0.8.0
????????4.1.32.Final
????
????
????????
????????????org.springframework.boot
????????????spring-boot-starter-web
????????????${springboot.verison}
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter
????????????${springboot.verison}
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-test
????????????${springboot.verison}
????????
????????
????????????com.alibaba
????????????druid
????????????${druid.version}
????????
????????
????????????org.mybatis.spring.boot
????????????mybatis-spring-boot-starter
????????????${mybatis.version}
????????
????????
????????????com.baomidou
????????????mybatis-plus
????????????${mybatis-plus.version}
????????
????????
????????????org.apache.dubbo
????????????dubbo
????????????${dubbo.version}
????????????
????????????????
????????????????????spring
????????????????????org.springframework
????????????????
????????????
????????
????????
????????????org.apache.dubbo
????????????dubbo-spring-boot-starter
????????????${dubbo.version}
????????
????????
????????
????????????org.apache.dubbo
????????????dubbo-config-spring
????????????${dubbo.version}
????????
????????
????????????org.apache.dubbo
????????????dubbo-registry-nacos
????????????${dubbo.version}
????????
????????
????????
????????????io.seata
????????????seata-all
????????????${seata.version}
????????
????????
????????????com.alibaba.nacos
????????????nacos-client
????????????${nacos-client.verison}
????????
????????
????????
????????????com.alibaba.boot
????????????nacos-config-spring-boot-starter
????????????${nacos.version}
????????????
????????????????
????????????????????nacos-client
????????????????????com.alibaba.nacos
????????????????
????????????
????????
????????
????????????org.springframework.boot
????????????spring-boot-maven-plugin
????????????${springboot.verison}
????????
????????
????????????org.projectlombok
????????????lombok
????????????${lombok.version}
????????
????????
????????????io.netty
????????????netty-all
????????????${netty.version}
????????
????????
????????????com.alibaba.spring
????????????spring-context-support
????????????1.0.2
????????
????????
????????????org.apache.httpcomponents
????????????httpclient
????????????4.5
????????
????????
????????????mysql
????????????mysql-connector-java
????????????5.1.47
????????
????
????
????????
????????????
????????????????org.springframework.boot
????????????????spring-boot-maven-plugin
????????????
????????????
????????????????org.apache.maven.plugins
????????????????maven-deploy-plugin
????????????????
????????????????????true
????????????????
????????????
????????????
????????????????org.apache.maven.plugins
????????????????maven-compiler-plugin
????????????????
????????????????????${java.version}
????????????????????${java.version}
????????????????
????????????
????????
????
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184注意:
seata-all: 這個(gè)是seata 所需的主要依賴dubbo-spring-boot-starter: springboot dubbo的依賴其他的就不一一介紹,其他的一目了然,就知道是干什么的。
3.8.2 application.properties配置
server.port=8102
spring.application.name=dubbo-account-example
#====================================Dubbo配置===============================================
dubbo.application.id= dubbo-account-example
dubbo.application.name= dubbo-account-example
dubbo.protocol.id=dubbo
dubbo.protocol.name=dubbo
dubbo.registry.id=dubbo-account-example-registry
dubbo.registry.address=nacos://192.168.10.200:8848
dubbo.protocol.port=20880
dubbo.application.qosEnable=false
dubbo.config-center.address=nacos://192.168.10.200:8848
dubbo.metadata-report.address=nacos://192.168.10.200:8848
#====================================mysql 配置============================================
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.10.200:3306/seata?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=lidong
spring.datasource.password=cwj887766@@
#=====================================mybatis 配置======================================
mybatis.mapper-locations=classpath*:/mapper/*.xml
123456789101112131415161718192021222324253.8.3 registry.conf 配置 (naocs的配置)
registry {
# file nacos
type = "nacos"
nacos {
serverAddr = "192.168.10.200"
namespace = "public"
cluster = "default"
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk、consul
type = "nacos"
file {
name = "file.conf"
}
nacos {
serverAddr = "192.168.10.200"
namespace = "public"
cluster = "default"
}
}
12345678910111213141516171819202122232425262728293.8.5 SeataAutoConfig 配置
package?io.seata.samples.integration.account.config;
import?com.alibaba.druid.pool.DruidDataSource;
import?io.seata.rm.datasource.DataSourceProxy;
import?io.seata.spring.annotation.GlobalTransactionScanner;
import?org.apache.ibatis.session.SqlSessionFactory;
import?org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import?org.mybatis.spring.SqlSessionFactoryBean;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
import?org.springframework.context.annotation.Primary;
import?org.springframework.core.io.support.PathMatchingResourcePatternResolver;
/**
?*?@Author:?llidong
?*?@Description??seata?global?configuration
?*?@Date?Created?in?2019/9/05?10:28
?*/
@Configuration
public?class?SeataAutoConfig?{
????/**
?????*?autowired?datasource?config
?????*/
????@Autowired
????private?DataSourceProperties?dataSourceProperties;
????/**
?????*?init?durid?datasource
?????*
?????*?@Return:?druidDataSource??datasource?instance
?????*/
????@Bean
????@Primary
????public?DruidDataSource?druidDataSource(){
????????DruidDataSource?druidDataSource?=?new?DruidDataSource();
????????druidDataSource.setUrl(dataSourceProperties.getUrl());
????????druidDataSource.setUsername(dataSourceProperties.getUsername());
????????druidDataSource.setPassword(dataSourceProperties.getPassword());
????????druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
????????druidDataSource.setInitialSize(0);
????????druidDataSource.setMaxActive(180);
????????druidDataSource.setMaxWait(60000);
????????druidDataSource.setMinIdle(0);
????????druidDataSource.setValidationQuery("Select?1?from?DUAL");
????????druidDataSource.setTestOnBorrow(false);
????????druidDataSource.setTestOnReturn(false);
????????druidDataSource.setTestWhileIdle(true);
????????druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
????????druidDataSource.setMinEvictableIdleTimeMillis(25200000);
????????druidDataSource.setRemoveAbandoned(true);
????????druidDataSource.setRemoveAbandonedTimeout(1800);
????????druidDataSource.setLogAbandoned(true);
????????return?druidDataSource;
????}
????/**
?????*?init?datasource?proxy
?????*?@Param:?druidDataSource??datasource?bean?instance
?????*?@Return:?DataSourceProxy??datasource?proxy
?????*/
????@Bean
????public?DataSourceProxy?dataSourceProxy(DruidDataSource?druidDataSource){
????????return?new?DataSourceProxy(druidDataSource);
????}
????/**
?????*?init?mybatis?sqlSessionFactory
?????*?@Param:?dataSourceProxy??datasource?proxy
?????*?@Return:?DataSourceProxy??datasource?proxy
?????*/
????@Bean
????public?SqlSessionFactory?sqlSessionFactory(DataSourceProxy?dataSourceProxy)?throws?Exception?{
????????SqlSessionFactoryBean?factoryBean?=?new?SqlSessionFactoryBean();
????????factoryBean.setDataSource(dataSourceProxy);
????????factoryBean.setMapperLocations(new?PathMatchingResourcePatternResolver()
????????????????.getResources("classpath*:/mapper/*.xml"));
????????return?factoryBean.getObject();
????}
????/**
?????*?init?global?transaction?scanner
?????*
?????*?@Return:?GlobalTransactionScanner
?????*/
????@Bean
????public?GlobalTransactionScanner?globalTransactionScanner(){
????????return?new?GlobalTransactionScanner("account-gts-seata-example",?"account-service-seata-service-group");
????}
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394其中:
@Bean
????public?GlobalTransactionScanner?globalTransactionScanner(){
????????return?new?GlobalTransactionScanner("account-gts-seata-example",?"account-service-seata-service-group");
????}
1234
GlobalTransactionScanner: 初始化全局的事務(wù)掃描器?/**
?????*?Instantiates?a?new?Global?transaction?scanner.
?????*
?????*?@param?applicationId??the?application?id
?????*?@param?txServiceGroup?the?default?server?group
?????*/
????public?GlobalTransactionScanner(String?applicationId,?String?txServiceGroup)?{
????????this(applicationId,?txServiceGroup,?DEFAULT_MODE);
????}
123456789
applicationId :為應(yīng)用id 這里我傳入的是 account-gts-seata-exampletxServiceGroup: 默認(rèn)server的分組 這里我傳入的是 account-service-seata-service-group這個(gè)和我們前面在nacos 配置的是保存一致。DEFAULT_MODE:默認(rèn)的事務(wù)模式 為AT_MODE + MT_MODE 3.8.6 AccountExampleApplication 啟動(dòng)類的配置
package?io.seata.samples.integration.account;
import?org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import?org.mybatis.spring.annotation.MapperScan;
import?org.springframework.boot.SpringApplication;
import?org.springframework.boot.autoconfigure.SpringBootApplication;
import?org.springframework.boot.context.config.ConfigFileApplicationListener;
@SpringBootApplication(scanBasePackages?=?"io.seata.samples.integration.account")
@MapperScan({"io.seata.samples.integration.account.mapper"})
@EnableDubbo(scanBasePackages?=?"io.seata.samples.integration.account")
public?class?AccountExampleApplication?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(AccountExampleApplication.class,?args);
????}
}
1234567891011121314151617181920
@EnableDubbo等同于@DubboComponentScan和@EnableDubboConfig組合@DubboComponentScan掃描 classpaths 下類中添加了@Service和@Reference將自動(dòng)注入到spring beans中。@EnableDubboConfig 掃描的dubbo的外部化配置。 4 啟動(dòng)所有的sample模塊
啟動(dòng)
samples-account、samples-order、samples-storage、samples-business并且在nocos的控制臺(tái)查看注冊(cè)情況: http://192.168.10.200:8848/nacos/#/serviceManagement
我們可以看到上面的服務(wù)都已經(jīng)注冊(cè)成功。
5 測試
5. 1 發(fā)送一個(gè)下單請(qǐng)求
使用postman 發(fā)送 :http://localhost:8104/business/dubbo/buy
參數(shù):
{
????"userId":"1",
????"commodityCode":"C201901140001",
????"name":"fan",
????"count":50,
????"amount":"100"
}
1234567返回
{
????"status":?200,
????"message":?"成功",
????"data":?null
}
12345這時(shí)候控制臺(tái):
2019-09-05 12:17:34.097 INFO 21860?---?[nio-8104-exec-4] i.s.s.i.c.controller.BusinessController ?:?請(qǐng)求參數(shù):BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)
2019-09-05?12:17:34.146??INFO?21860?---?[nio-8104-exec-4]?i.seata.tm.api.DefaultGlobalTransaction??:?Begin?new?global?transaction?[192.168.10.200:8091:2021380638]
2019-09-05?12:17:34.150??INFO?21860?---?[nio-8104-exec-4]?i.s.s.i.c.service.BusinessServiceImpl????:?開始全局事務(wù),XID?=?192.168.10.200:8091:2021380638
2019-09-05?12:17:36.966??INFO?21860?---?[nio-8104-exec-4]?i.seata.tm.api.DefaultGlobalTransaction??:?[192.168.10.200:8091:2021380638]?commit?status:Committed
1234事務(wù)提交成功,
我們來看一下數(shù)據(jù)庫數(shù)據(jù)變化
t_account
t_order
t_storage
數(shù)據(jù)沒有問題。
5.2 測試回滾
我們
samples-business將BusinessServiceImpl的handleBusiness2下面的代碼去掉注釋if?(!flag)?{
??throw?new?RuntimeException("測試拋異常后,分布式事務(wù)回滾!");
}
123使用postman 發(fā)送 :http://localhost:8104/business/dubbo/buy2
.響應(yīng)結(jié)果:
{
????"timestamp":?"2019-09-05T04:29:34.178+0000",
????"status":?500,
????"error":?"Internal?Server?Error",
????"message":?"測試拋異常后,分布式事務(wù)回滾!",
????"path":?"/business/dubbo/buy"
}
1234567控制臺(tái)
2019-09-05 12:29:32.836 INFO 17264 ---?[nio-8104-exec-2] i.s.s.i.c.controller.BusinessController ?:?請(qǐng)求參數(shù):BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)
2019-09-05?12:29:32.843??INFO?17264?---?[nio-8104-exec-2]?i.s.common.loader.EnhancedServiceLoader??:?load?ContextCore[null]?extension?by?class[io.seata.core.context.ThreadLocalContextCore]
2019-09-05?12:29:32.848??INFO?17264?---?[nio-8104-exec-2]?i.s.common.loader.EnhancedServiceLoader??:?load?TransactionManager[null]?extension?by?class[io.seata.tm.DefaultTransactionManager]
2019-09-05?12:29:32.849??INFO?17264?---?[nio-8104-exec-2]?io.seata.tm.TransactionManagerHolder?????:?TransactionManager?Singleton?io.seata.tm.DefaultTransactionManager@461585ac
2019-09-05?12:29:32.859??INFO?17264?---?[nio-8104-exec-2]?i.s.common.loader.EnhancedServiceLoader??:?load?LoadBalance[null]?extension?by?class[io.seata.discovery.loadbalance.RandomLoadBalance]
2019-09-05?12:29:32.893??INFO?17264?---?[nio-8104-exec-2]?i.seata.tm.api.DefaultGlobalTransaction??:?Begin?new?global?transaction?[192.168.10.200:8091:2021380674]
2019-09-05?12:29:32.897??INFO?17264?---?[nio-8104-exec-2]?i.s.s.i.c.service.BusinessServiceImpl????:?開始全局事務(wù),XID?=?192.168.10.200:8091:2021380674
2019-09-05?12:29:34.143??INFO?17264?---?[nio-8104-exec-2]?i.seata.tm.api.DefaultGlobalTransaction??:?[192.168.10.200:8091:2021380674]?rollback?status:Rollbacked
2019-09-05?12:29:34.158?ERROR?17264?---?[nio-8104-exec-2]?o.a.c.c.C.[.[.[/].[dispatcherServlet]????:?Servlet.service()?for?servlet?[dispatcherServlet]?in?context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException:?測試拋異常后,分布式事務(wù)回滾!] with root cause
java.lang.RuntimeException:?測試拋異常后,分布式事務(wù)回滾!
?at?io.seata.samples.integration.call.service.BusinessServiceImpl.handleBusiness(BusinessServiceImpl.java:60)?~[classes/:na]
?at?io.seata.samples.integration.call.service.BusinessServiceImpl$$FastClassBySpringCGLIB$$2ab3d645.invoke()?~[classes/:na]
?at?org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)?~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)?~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)?~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?io.seata.spring.annotation.GlobalTransactionalInterceptor$1.execute(GlobalTransactionalInterceptor.java:104)?~[seata-all-0.8.0.jar:0.8.0]
?at?io.seata.tm.api.TransactionalTemplate.execute(TransactionalTemplate.java:64)?~[seata-all-0.8.0.jar:0.8.0]
?at?io.seata.spring.annotation.GlobalTransactionalInterceptor.handleGlobalTransaction(GlobalTransactionalInterceptor.java:101)?~[seata-all-0.8.0.jar:0.8.0]
?at?io.seata.spring.annotation.GlobalTransactionalInterceptor.invoke(GlobalTransactionalInterceptor.java:76)?~[seata-all-0.8.0.jar:0.8.0]
?at?org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)?~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)?~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?io.seata.samples.integration.call.service.BusinessServiceImpl$$EnhancerBySpringCGLIB$$cb43f7ab.handleBusiness()?~[classes/:na]
?at?io.seata.samples.integration.call.controller.BusinessController.handleBusiness(BusinessController.java:37)?~[classes/:na]
?at?sun.reflect.NativeMethodAccessorImpl.invoke0(Native?Method)?~[na:1.8.0_144]
?at?sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)?~[na:1.8.0_144]
?at?sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)?~[na:1.8.0_144]
?at?java.lang.reflect.Method.invoke(Method.java:498)?~[na:1.8.0_144]
?at?org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?javax.servlet.http.HttpServlet.service(HttpServlet.java:660)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?javax.servlet.http.HttpServlet.service(HttpServlet.java:741)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)?~[tomcat-embed-websocket-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)?[na:1.8.0_144]
?at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)?[na:1.8.0_144]
?at?org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)?[tomcat-embed-core-9.0.19.jar:9.0.19]
?at?java.lang.Thread.run(Thread.java:748)?[na:1.8.0_144]
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980我們查看數(shù)據(jù)庫數(shù)據(jù),已經(jīng)回滾,和上面的數(shù)據(jù)一致。
springboot+redis+Interceptor+自定義annotation實(shí)現(xiàn)接口自動(dòng)冪等
JDK 16 即將發(fā)布,看完這些新特性,我感覺已經(jīng)學(xué)不動(dòng)了..
點(diǎn)擊閱讀全文前往微服務(wù)電商教程

這是時(shí)候naocs 就正常啟動(dòng)了。




t_order
t_storage
數(shù)據(jù)沒有問題。