springboot-dubbo-seata-zk
springboot-dubbo-seata-zk copied to clipboard
SpringBoot+Zookeeper+Seata实现Dubbo分布式事务管理
é¢æå·²ç»åè¿ä¸ç¯SpringBoot+Nacos+Seataå®ç°Dubboåå¸å¼äºå¡ç®¡ççæç« ï¼ä»å¤©ä¸ºä»ä¹è¿è¦åè¿ç¯å¢ï¼æ¯å 为好å¤å
¬å¸è¿å¨ç¨Zookeeper
ä½ä¸ºDubbo
ç注åä¸å¿åé
ç½®ä¸å¿å¨å¤§è§æ¨¡ä½¿ç¨ï¼è¿æ²¡æå®å
¨è¿ç§»å°Nacos
ä¸æ¥ï¼æ以Seata
ç注åä¸å¿åé
ç½®ä¹æ¯æ¯æZookeeper
ï¼ä½æ¯å®æ¹æ²¡æå®æ´ç使ç¨æç¨ï¼å æ¤ï¼åè¿ç¯ä¸»è¦ä¸ºäºå¸®å©ä½¿ç¨Zookeeper
çç¨æ·ä¹å¯ä»¥è½»æ¾ä½¿ç¨Seata
ã
1.ç®ä»
æ¬æ主è¦ä»ç»SpringBoot2.1.5 + Dubbo 2.7.3 + Mybatis 3.4.2 + Zookeeper 3.4.14 +Seata 1.4.0æ´åæ¥å®ç°Dubboåå¸å¼äºå¡ç®¡çï¼ä½¿ç¨Zookeeper ä½ä¸º DubboåSeataç注åä¸å¿åé ç½®ä¸å¿,ä½¿ç¨ MySQL æ°æ®åºå MyBatisæ¥æä½æ°æ®ã
å¦æä½ è¿å¯¹SpringBoot
ãDubbo
ãZookeeper
ãSeata
ã Mybatis
ä¸æ¯å¾äºè§£çè¯ï¼è¿éæ为大家æ´ç个å®ä»¬çå®ç½ç½ç«ï¼å¦ä¸
-
SpringBootï¼https://spring.io/projects/spring-boot
-
Dubboï¼http://dubbo.apache.org/en-us/
-
Zookeeperï¼https://zookeeper.apache.org/
å¨è¿éæ们就ä¸ä¸ä¸ªä¸ä¸ªä»ç»å®ä»¬æ¯æä¹ä½¿ç¨ååçï¼è¯¦ç»è¯·å¦ä¹ å®æ¹ææ¡£ï¼å¨è¿éæå°å¼å§å¯¹å®ä»¬è¿è¡æ´åï¼å®æä¸ä¸ªç®åçæ¡ä¾ï¼æ¥è®©å¤§å®¶äºè§£Seata
æ¥å®ç°Dubbo
åå¸å¼äºå¡ç®¡ççåºæ¬æµç¨ã
2.ç¯å¢åå¤
2.1 ä¸è½½Zookeeper并å®è£ å¯å¨
Zookeeperä¸è½½ï¼https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/
Zookeeper å¿«éå ¥é¨ï¼http://zookeeper.apache.org/doc/r3.4.14/zookeeperStarted.html
å¨åæºæ¨¡å¼ä¸å¯å¨Zookeeper
é常ç®åã
æ们å°ä¸è½½çæ件解å缩å°æå®ç®å½å¦ï¼E:\tools\zookeeper-3.4.14
è¦å¯å¨
Zookeeper
ï¼æ们éè¦ä¸ä¸ªé
ç½®æ件ãä¸é¢æ¯ä¸ä¸ªç¤ºä¾ï¼å¨conf/zoo.cfg:
tickTime=2000
initLimit=10
syncLimit=5
clientPort=2181
dataDir=E:\\tools\\zookeeper-3.4.14\\data
dataLogDir=E:\\tools\\zookeeper-3.4.14\\logs
以ä¸é ç½®ç详ç»è¯´æå¦ä¸ï¼
-
tickTime
ï¼ç¨äºé ç½® ZooKeeper ä¸æå°æ¶é´åä½çé¿åº¦ï¼å¾å¤è¿è¡æ¶çæ¶é´é´éé½æ¯ä½¿ç¨ tickTime çåæ°æ¥è¡¨ç¤ºçãä¾å¦ï¼ZooKeeper ä¸ä¼è¯çæå°è¶ æ¶æ¶é´é»è®¤æ¯ 2*tickTimeã -
initLimit
ï¼è¯¥åæ°æé»è®¤å¼: 10ï¼å³è¡¨ç¤ºæ¯åæ° tickTime å¼å¾10åï¼å¿ é¡»é ç½®ï¼ä¸éè¦é ç½®ä¸ä¸ªæ£æ´æ°ï¼ä¸æ¯æç³»ç»å±æ§æ¹å¼é ç½®ã该åæ°ç¨äºé ç½® Leader æå¡å¨çå¾ Follower å¯å¨ï¼å¹¶å®ææ°æ®åæ¥çæ¶é´ãFollwer æå¡å¨åå¯å¨è¿ç¨ä¸ï¼ä¼ä¸ Leader 建ç«è¿æ¥å¹¶å®æ对æ°æ®çåæ¥ï¼ä»èç¡®å®èªå·±å¯¹å¤æä¾æå¡çå ¶å®ç¶æãLeader æå¡å¨å 许 Follower å¨ initLimit æ¶é´å å®æè¿ä¸ªå·¥ä½ã é常æ åµä¸ï¼è¿ç»´äººåä¸ç¨å¤ªå¨æè¿ä¸ªåæ°çé ç½®ï¼ä½¿ç¨é»è®¤å¼å³å¯ãä½å¦æéç ZooKeeper é群管ççæ°æ®éå¢å¤§ï¼Follower æå¡å¨åå¯å¨çæ¶åï¼ä» Leader ä¸è¿è¡åæ¥æ°æ®çæ¶é´ä¹ä¼ç¸åºåé¿ï¼äºæ¯æ æ³å¨è¾ççæ¶é´å®ææ°æ®åæ¥ãå æ¤ï¼å¨è¿ç§æ åµä¸ï¼æå¿ è¦éå½è°å¤§è¿ä¸ªåæ°ã -
syncLimit
:该åæ°æé»è®¤å¼: 5ï¼å³è¡¨ç¤ºåæ° tickTime å¼å¾5åï¼å¿ é¡»é ç½®ï¼ä¸éè¦é ç½®ä¸ä¸ªæ£æ´æ°ï¼ä¸æ¯æç³»ç»å±æ§é ç½®ã该åæ°ç¨äºé ç½® Leader æå¡å¨å Follower æå¡å¨ä¹é´è¿è¡å¿è·³æ£æµçæ大延æ¶æ¶é´ãå¨ ZooKeeper é群è¿è¡è¿ç¨ä¸ï¼Leader æå¡å¨ä¼ä¸ææç Follower è¿è¡å¿è·³æ£æµæ¥ç¡®å®è¯¥æå¡å¨æ¯å¦åæ´»ãå¦æ Leader æå¡å¨å syncLimit æ¶é´å æ æ³è·åå° Follower çå¿è·³æ£æµååºï¼é£ä¹ Leader å°±ä¼è®¤ä¸ºè¯¥ Follower å·²ç»è±ç¦»äºåèªå·±çåæ¥ãé常æ åµä¸ï¼è¿ç»´äººå使ç¨è¯¥åæ°çé»è®¤å¼å³å¯ï¼ä½å¦æé¨ç½²ZooKeeper é群çç½ç»ç¯å¢è´¨éè¾ä½ï¼ä¾å¦ç½ç»å»¶æ¶è¾å¤§æ丢å 严éï¼ï¼é£ä¹å¯ä»¥éå½è°å¤§è¿ä¸ªåæ°ã -
dataDir
: 该åæ°æé»è®¤å¼: dataDirï¼å¯ä»¥ä¸é ç½®ï¼ä¸æ¯æç³»ç»å±æ§æ¹å¼é ç½®ãåæ° dataLogDir ç¨äºé ç½® ZooKeeper æå¡å¨åå¨äºå¡æ¥å¿æ件çç®å½ãé»è®¤æ åµä¸ï¼ZooKeeper ä¼å°äºå¡æ¥å¿æ件åå¿«ç §æ°æ®åå¨å¨åä¸ç®å½ä¸ï¼åºè¯¥å°½éå°è¿ä¸¤è çç§åºåºåå¼æ¥ãå¦å¤ï¼å¦ææ¡ä»¶å 许ï¼å¯ä»¥å°äºå¡æ¥å¿çåå¨ä½ç½®é ç½®å¨ä¸ä¸ªåç¬çç£çä¸ãäºå¡æ¥å¿è®°å½å¯¹äºç£ççæ§è½è¦æ±é常é«ï¼ä¸ºäºä¿è¯æ°æ®çä¸è´æ§ï¼ZooKeeper å¨è¿å客æ·ç«¯äºå¡è¯·æ±ç¸åºä¹åï¼å¿ é¡»å°æ¬æ¬¡è¯·æ±å¯¹åºçäºå¡æ¥å¿åå ¥å°ç£çä¸ãå æ¤ï¼äºå¡æ¥å¿åå ¥çæ§è½ç´æ¥å³å®äº ZooKeeper å¨å¤çäºå¡è¯·æ±æ¶çååãé对åä¸åç£ççå ¶ä»å¹¶å读åæä½ï¼ä¾å¦ ZooKeeper è¿è¡æ¶æ¥å¿è¾åºåæä½ç³»ç»èªèº«ç读åçï¼ï¼å°¤å ¶æ¯æ°æ®å¿«ç §æä½ï¼ä¼æ大å°å½±åäºå¡æ¥å¿çåæ§è½ãå æ¤å°½éç»äºå¡æ¥å¿çè¾åºé ç½®ä¸ä¸ªåç¬çç£çææè½½ç¹ï¼å°æ大å°æå ZooKeeper çæ´ä½æ§è½ã -
clientPort
:ç¨äºé ç½®å½åæå¡å¨å¯¹å¤çæå¡ç«¯å£ï¼å®¢æ·ç«¯ä¼éè¿è¯¥ç«¯å£å ZooKeeper æå¡å¨å建è¿æ¥ï¼ä¸è¬è®¾ç½®ä¸º2181ãæ¯å° ZooKeeper é½å¯ä»¥é 置任æå¯ç¨ç端å£ï¼åæ¶é群ä¸çæææå¡å¨ä¸éè¦ä¿æ clientPort 端å£ä¸è´ã该åæ°æ é»è®¤å¼ï¼å¿ é¡»é ç½®ã
dataLogDir
:åå¨æ¥å¿çç®å½
å¯å¨Zookeeperï¼
bin/zkServer.cmd start
è¿æ¯æ¶å
ZooKeeper
æå¡å°±æ£å¸¸å¯å¨äºã
æ们å¯ä»¥ä½¿å®¢æ·ç«¯å»æ¥è¿æ¥
bin/zkCli.cmd
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
æ们æ¯ç¨ls
æ¥è¯¢èç¹æ
åµ
ls /
é»è®¤æä¸ä¸ª
zookeeoer
èç¹ã
2.2 ä¸è½½seata1.4.0 并å®è£ å¯å¨
2.2.1 å¨ Seata Release ä¸è½½ææ°çç Seata Server 并解åå¾å°å¦ä¸ç®å½ï¼
.
âââbin
âââconf
âââlib
2.2.2 ä¿®æ¹ conf/registry.conf åfile.confé ç½®ï¼
ç®åseataæ¯æå¦ä¸çfileãnacos ãapolloãzkãconsulç注åä¸å¿åé
ç½®ä¸å¿ãè¿éæ们以zk
为ä¾ã
å° type æ¹ä¸º zk
registry {
# file zk
type = "zk"
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
file {
name = "file.conf"
}
}
config {
# fileãnacos ãapolloãzkãconsul
type = "zk"
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
file {
name = "file.conf"
}
}
- serverAddr = "127.0.0.1:2181" ï¼zk çå°å
- cluster = "default" ï¼é群设置为é»è®¤
default
- session.timeout = 6000 ï¼ä¼è¯çè¶ æ¶æ¶é´
- connect.timeout = 2000ï¼è¿æ¥çè¶ æ¶æ¶é´
file.conf é ç½®
## transaction log store
store {
## store mode: fileãdb
mode = "db"
## file store
file {
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
max-branch-session-size = 16384
# globe session size , if exceeded throws exceptions
max-global-session-size = 512
# file buffer size , if exceeded allocate new buffer
file-write-buffer-cache-size = 16384
# when recover batch read size
session.reload.read_size = 100
# async, sync
flush-disk-mode = async
}
## database store
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "druid"
## mysql/oracle/h2/oceanbase etc.
db-type = "mysql"
driver-class-name = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "root"
password = "123456"
min-conn = 1
max-conn = 3
global.table = "global_table"
branch.table = "branch_table"
lock-table = "lock_table"
query-limit = 100
}
}
主è¦ä¿®æ¹äºstore.mode
为db
,è¿ææ°æ®åºç¸å
³çé
ç½®
2.2.3 ä¿®æ¹ conf/nacos-config.txté 置为zk-config.properties
service.vgroupMapping.order-service-seata-service-group=default
service.vgroupMapping.account-service-seata-service-group=default
service.vgroupMapping.storage-service-seata-service-group=default
service.vgroupMapping.business-service-seata-service-group=default
store.mode=db
store.db.datasource=dbcp
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true
store.db.user=root
store.db.password=123456
store.db.minConn=1
store.db.maxConn=3
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
è¿é主è¦ä¿®æ¹äºå¦ä¸å 项ï¼
- store.mode :åå¨æ¨¡å¼ é»è®¤file è¿éæä¿®æ¹ä¸ºdb æ¨¡å¼ ï¼å¹¶ä¸éè¦ä¸ä¸ªè¡¨
global_table
ãbranch_table
ålock_table
- store.db.driver-class-nameï¼ é»è®¤æ²¡æï¼ä¼æ¥éãæ·»å äº
com.mysql.jdbc.Driver
- store.db.datasource=dbcp ï¼æ°æ®æº dbcp
- store.db.db-type=mysql : åå¨æ°æ®åºçç±»å为
mysql
- store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true : ä¿®æ¹ä¸ºèªå·±çæ°æ®åº
url
ãport
ãæ°æ®åºå称
- store.db.user=root :æ°æ®åºçè´¦å·
- store.db.password=123456 :æ°æ®åºçå¯ç
- service.vgroupMapping.order-service-seata-service-group=default
- service.vgroupMapping.account-service-seata-service-group=default
- service.vgroupMapping.storage-service-seata-service-group=default
- service.vgroupMapping.business-service-seata-service-group=default
db模å¼ä¸çæéçä¸ä¸ªè¡¨çæ°æ®åºèæ¬ä½äºseata\conf\db_store.sql
global_table
ç表ç»æ
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;
branch_table
ç表ç»æ
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;
lock_table
ç表ç»æ
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`)
);
2.2.4 å° Seata é 置添å å° Zookeeper ä¸
ç¨äºå®æ¹åªæä¾äºnacosçèæ¬é
ç½®ãæè¿ç¨javaå®ç°äºå° Seata é
置添å å° Zookeeper ä¸ã
æè¿éåèäºZookeeperConfiguration
çæºç å®ç°ç导å
¥å°zkçåå§å代ç ã
éè¿æ¥ç以ä¸æºä»£ç æ们å¯ä»¥çåº
zk模å¼ä¸æ°æ®çåå¨æ ¼å¼ã并ä¸ä½¿ç¨çzkçæ°¸ä¹
èç¹åå¨ã
2.4.2.1.åå»ºæ ¹èç¹ï¼å
¶ä¸æ ¹èç¹ä¸º/seata
public ZookeeperConfiguration() {
if (zkClient == null) {
synchronized (ZookeeperConfiguration.class) {
if (zkClient == null) {
ZkSerializer zkSerializer = getZkSerializer();
String serverAddr = FILE_CONFIG.getConfig(FILE_CONFIG_KEY_PREFIX + SERVER_ADDR_KEY);
int sessionTimeout = FILE_CONFIG.getInt(FILE_CONFIG_KEY_PREFIX + SESSION_TIMEOUT_KEY, DEFAULT_SESSION_TIMEOUT);
int connectTimeout = FILE_CONFIG.getInt(FILE_CONFIG_KEY_PREFIX + CONNECT_TIMEOUT_KEY, DEFAULT_CONNECT_TIMEOUT);
zkClient = new ZkClient(serverAddr, sessionTimeout, connectTimeout, zkSerializer);
String username = FILE_CONFIG.getConfig(FILE_CONFIG_KEY_PREFIX + AUTH_USERNAME);
String password = FILE_CONFIG.getConfig(FILE_CONFIG_KEY_PREFIX + AUTH_PASSWORD);
if (!StringUtils.isBlank(username) && !StringUtils.isBlank(password)) {
StringBuilder auth = new StringBuilder(username).append(":").append(password);
zkClient.addAuthInfo("digest", auth.toString().getBytes());
}
}
}
if (!zkClient.exists(ROOT_PATH)) {
zkClient.createPersistent(ROOT_PATH, true);
}
}
}
注æï¼1.3.0 å å ¥äºZkSerializeråºååã
2.4.2.2æ·»å é ç½®çæ¹æ³
public boolean putConfig(String dataId, String content, long timeoutMills) {
FutureTask<Boolean> future = new FutureTask<>(() -> {
String path = ROOT_PATH + ZK_PATH_SPLIT_CHAR + dataId;
if (!zkClient.exists(path)) {
zkClient.create(path, content, CreateMode.PERSISTENT);
} else {
zkClient.writeData(path, content);
}
return true;
});
CONFIG_EXECUTOR.execute(future);
try {
return future.get(timeoutMills, TimeUnit.MILLISECONDS);
} catch (Exception e) {
LOGGER.error("putConfig {}, value: {} is error or timeout, exception: {}",
dataId, content, e.getMessage());
return false;
}
}
2.4.2.3 使ç¨java读åzk-config.propertiesé ç½®æ件
package io.seata.samples.integration.call;
import io.seata.config.zk.DefaultZkSerializer;
import lombok.extern.slf4j.Slf4j;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkMarshallingError;
import org.I0Itec.zkclient.serialize.ZkSerializer;
import org.apache.zookeeper.CreateMode;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Set;
/**
*
*/
@Slf4j
public class ZkDataInit {
private static volatile ZkClient zkClient;
public static void main(String[] args) {
if (zkClient == null) {
ZkSerializer zkSerializer = new DefaultZkSerializer();
zkClient = new ZkClient("127.0.0.1:2181", 6000, 2000, zkSerializer);
}
if (!zkClient.exists("/seata")) {
zkClient.createPersistent("/seata", true);
}
//è·åkey对åºçvalueå¼
Properties properties = new Properties();
// 使ç¨ClassLoaderå è½½propertiesé
ç½®æ件çæ对åºçè¾å
¥æµ
// 使ç¨properties对象å è½½è¾å
¥æµ
try {
File file = ResourceUtils.getFile("classpath:zk-config.properties");
InputStream in = new FileInputStream(file);
properties.load(in);
Set<Object> keys = properties.keySet();//è¿åå±æ§keyçéå
for (Object key : keys) {
boolean b = putConfig(key.toString(), properties.get(key).toString());
log.info(key.toString() + "=" + properties.get(key)+"result="+b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @param dataId
* @param content
* @return
*/
public static boolean putConfig(final String dataId, final String content) {
Boolean flag = false;
String path = "/seata/" + dataId;
if (!zkClient.exists(path)) {
zkClient.create(path, content, CreateMode.PERSISTENT);
flag = true;
} else {
zkClient.writeData(path, content);
flag = true;
}
return flag;
}
}
2.4.2.4 æ¥çzkçèç¹ç»æ
[zk: localhost:2181(CONNECTED) 10] ls /seata
[transport.threadFactory.clientSelectorThreadSize, transport.threadFactory.serve
rExecutorThreadPrefix, store.db.minConn, store.file.maxBranchSessionSize, client
.undo.logTable, transport.threadFactory.clientSelectorThreadPrefix, server.recov
ery.rollbackingRetryPeriod, store.db.datasource, transport.server, client.rm.rep
ortSuccessEnable, transport.enableClientBatchSendRequest, client.rm.lock.retryIn
terval, client.tm.commitRetryCount, client.undo.onlyCareUpdateColumns, store.db.
globalTable, store.redis.password, client.rm.lock.retryTimes, store.db.dbType, s
ervice.disableGlobalTransaction, server.recovery.timeoutRetryPeriod, service.vgr
oupMapping.manage-service-seata-service-group, transport.threadFactory.bossThrea
dSize, store.redis.queryLimit, store.file.maxGlobalSessionSize, store.db.maxConn
, server.recovery.committingRetryPeriod, store.db.queryLimit, store.mode, transp
ort.serialization, store.file.dir, transport.threadFactory.clientWorkerThreadPre
fix, client.rm.tableMetaCheckEnable, store.db.branchTable, transport.compressor,
store.db.user, store.db.url, transport.threadFactory.workerThreadSize, transpor
t.threadFactory.bossThreadPrefix, client.rm.reportRetryCount, store.db.driverCla
ssName, client.tm.degradeCheckAllowTimes, service.default.grouplist, server.maxR
ollbackRetryTimeout, client.tm.rollbackRetryCount, client.undo.logSerialization,
metrics.exporterList, service.vgroupMapping.sales-service-seata-service-group,
store.file.flushDiskMode, metrics.enabled, client.rm.asyncCommitBufferLimit, sto
re.db.password, store.file.sessionReloadReadSize, metrics.exporterPrometheusPort
, server.rollbackRetryTimeoutUnlockEnable, client.tm.degradeCheck, store.redis.d
atabase, store.redis.host, client.undo.dataValidation, client.rm.lock.retryPolic
yBranchRollbackOnConflict, server.recovery.asynCommittingRetryPeriod, metrics.re
gistryType, client.log.exceptionRate, transport.threadFactory.shareBossWorker, c
lient.rm.sqlParserType, service.enableDegrade, transport.threadFactory.workerThr
eadPrefix, transport.type, service.vgroupMapping.infoManage-service-seata-servic
e-group, server.maxCommitRetryTimeout, transport.heartbeat, store.db.lockTable,
server.undo.logSaveDays, server.undo.logDeletePeriod, client.rm.sagaBranchRegist
erEnable, store.redis.port, store.db.maxWait, store.redis.minConn, transport.shu
tdown.wait, store.redis.maxConn, store.file.fileWriteBufferCacheSize, client.tm.
degradeCheckPeriod]
ææçé ç½®é½å·²ç»å¯¼å ¥æåã
2.2.5 å¯å¨ Seata Server
使ç¨db 模å¼å¯å¨
cd ..
bin/seata-server.bat -m db
è¿æ¶åå¨ zookeeper çæ们å¨å®¢æ·ç«¯å¯ä»¥ä½¿ä½¿ç¨å½ä»¤æ¥è¯¢èç¹
[zk: localhost:2181(CONNECTED) 4] ls /
registry zookeeper seata
[zk: localhost:2181(CONNECTED) 4] ls /registry/zk/default
[192.168.10.108:8091]
çå°è¿ï¼seata server就已ç»æ建æå
3 æ¡ä¾åæ
åèå®ç½ä¸ç¨æ·è´ä¹°ååçä¸å¡é»è¾ãæ´ä¸ªä¸å¡é»è¾ç±4个微æå¡æä¾æ¯æï¼
- åºåæå¡ï¼æ£é¤ç»å®ååçåå¨æ°éã
- 订åæå¡ï¼æ ¹æ®è´ä¹°è¯·æ±å建订åã
- å¸æ·æå¡ï¼åè®°ç¨æ·å¸æ·çä½é¢ã
- ä¸å¡æå¡ï¼å¤çä¸å¡é»è¾ã
请æ±é»è¾æ¶æ
3.1 githubå°å
springboot-dubbo-seataï¼https://github.com/lidong1665/springboot-dubbo-seata-zk
-
samples-common ï¼å ¬å ±æ¨¡å
-
samples-account ï¼ç¨æ·è´¦å·æ¨¡å
-
samples-order ï¼è®¢å模å
-
samples-storage ï¼åºå模å
-
samples-business ï¼ä¸å¡æ¨¡å
3.2 è´¦æ·æå¡ï¼AccountDubboService
/**
* @Author: lidong
* @Description è´¦æ·æå¡æ¥å£
* @Date Created in 2019/9/5 16:37
*/
public interface AccountDubboService {
/**
* ä»è´¦æ·æ£é±
*/
ObjectResponse decreaseAccount(AccountDTO accountDTO);
}
3.3 订åæå¡ï¼OrderDubboService
/**
* @Author: lidong
* @Description 订åæå¡æ¥å£
* @Date Created in 2019/9/5 16:28
*/
public interface OrderDubboService {
/**
* å建订å
*/
ObjectResponse<OrderDTO> createOrder(OrderDTO orderDTO);
}
3.4 åºåæå¡ï¼StorageDubboService
/**
* @Author: lidong
* @Description åºåæå¡
* @Date Created in 2019/9/5 16:22
*/
public interface StorageDubboService {
/**
* æ£ååºå
*/
ObjectResponse decreaseStorage(CommodityDTO commodityDTO);
}
3.5 ä¸å¡æå¡ï¼BusinessService
/**
* @Author: lidong
* @Description
* @Date Created in 2019/9/5 17:17
*/
public interface BusinessService {
/**
* åºå¤çä¸å¡æå¡
* @param businessDTO
* @return
*/
ObjectResponse handleBusiness(BusinessDTO businessDTO);
}
ä¸å¡é»è¾çå ·ä½å®ç°ä¸»è¦ä½ç°å¨ 订åæå¡çå®ç°åä¸å¡æå¡çå®ç°
订åæå¡çå®ç°
@Service
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements ITOrderService {
@Reference(version = "1.0.0")
private AccountDubboService accountDubboService;
/**
* å建订å
* @Param: OrderDTO 订å对象
* @Return: OrderDTO 订å对象
*/
@Override
public ObjectResponse<OrderDTO> createOrder(OrderDTO orderDTO) {
ObjectResponse<OrderDTO> response = new ObjectResponse<>();
//æ£åç¨æ·è´¦æ·
AccountDTO accountDTO = new AccountDTO();
accountDTO.setUserId(orderDTO.getUserId());
accountDTO.setAmount(orderDTO.getOrderAmount());
ObjectResponse objectResponse = accountDubboService.decreaseAccount(accountDTO);
//çæ订åå·
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;
}
}
æ´ä¸ªä¸å¡çå®ç°é»è¾
@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;
/**
* å¤çä¸å¡é»è¾
* @Param:
* @Return:
*/
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-gts-seata-example")
@Override
public ObjectResponse handleBusiness(BusinessDTO businessDTO) {
log.info("å¼å§å
¨å±äºå¡ï¼XID = " + RootContext.getXID());
ObjectResponse<Object> objectResponse = new ObjectResponse<>();
//1ãæ£ååºå
CommodityDTO commodityDTO = new CommodityDTO();
commodityDTO.setCommodityCode(businessDTO.getCommodityCode());
commodityDTO.setCount(businessDTO.getCount());
ObjectResponse storageResponse = storageDubboService.decreaseStorage(commodityDTO);
//2ãå建订å
OrderDTO orderDTO = new OrderDTO();
orderDTO.setUserId(businessDTO.getUserId());
orderDTO.setCommodityCode(businessDTO.getCommodityCode());
orderDTO.setOrderCount(businessDTO.getCount());
orderDTO.setOrderAmount(businessDTO.getAmount());
ObjectResponse<OrderDTO> response = orderDubboService.createOrder(orderDTO);
//æå¼æ³¨éæµè¯äºå¡åçå¼å¸¸åï¼å
¨å±åæ»åè½
// if (!flag) {
// throw new RuntimeException("æµè¯æå¼å¸¸åï¼åå¸å¼äºå¡åæ»ï¼");
// }
if (storageResponse.getStatus() != 200 || response.getStatus() != 200) {
throw new DefaultException(RspStatusEnum.FAIL);
}
objectResponse.setStatus(RspStatusEnum.SUCCESS.getCode());
objectResponse.setMessage(RspStatusEnum.SUCCESS.getMessage());
objectResponse.setData(response.getData());
return objectResponse;
}
}
3.6 使ç¨seataçåå¸å¼äºå¡è§£å³æ¹æ¡å¤çdubboçåå¸å¼äºå¡
æ们åªéè¦å¨ä¸å¡å¤ççæ¹æ³handleBusiness
æ·»å ä¸ä¸ªæ³¨è§£ @GlobalTransactional
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-gts-seata-example")
@Override
public ObjectResponse handleBusiness(BusinessDTO businessDTO) {
}
-
timeoutMills
: è¶ æ¶æ¶é´ -
name
ï¼äºå¡å称
3.7 åå¤æ°æ®åº
注æ: MySQLå¿
须使ç¨InnoDB engine
.
å建æ°æ®åº å¹¶å¯¼å ¥æ°æ®åºèæ¬
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;
ä¼çå°å¦ä¸ç4个表
+-------------------------+
| Tables_in_seata |
+-------------------------+
| t_account |
| t_order |
| t_storage |
| undo_log |
+-------------------------+
è¿é为äºç®åæå°è¿ä¸ªä¸å¼ 表å建å°ä¸ä¸ªåºä¸,使ç¨æ¯ä¸ä¸ªæ°æ®æºæ¥å®ç°ã
3.8 æ们以账å·æå¡samples-account
ä¸ºä¾ ï¼åæéè¦æ³¨æçé
置项ç®
3.8.1 å¼å ¥çä¾èµ
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>springboot-dubbo-seata</artifactId>
<packaging>pom</packaging>
<name>springboot-dubbo-seata</name>
<groupId>io.seata</groupId>
<version>1.0.0</version>
<description>Demo project for Spring Cloud Alibaba Dubbo</description>
<modules>
<module>samples-common</module>
<module>samples-account</module>
<module>samples-order</module>
<module>samples-storage</module>
<module>samples-business</module>
</modules>
<properties>
<springboot.verison>2.1.5.RELEASE</springboot.verison>
<java.version>1.8</java.version>
<druid.version>1.1.10</druid.version>
<mybatis.version>1.3.2</mybatis.version>
<mybatis-plus.version>2.3</mybatis-plus.version>
<lombok.version>1.16.22</lombok.version>
<dubbo.version>2.7.3</dubbo.version>
<seata.version>0.9.0</seata.version>
<netty.version>4.1.32.Final</netty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot.verison}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${springboot.verison}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${springboot.verison}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-config-spring -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-config-spring</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.seata/seata-all -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>${seata.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${springboot.verison}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.spring</groupId>
<artifactId>spring-context-support</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
注æï¼
-
seata-all
: è¿ä¸ªæ¯seata æéç主è¦ä¾èµã -
dubbo-spring-boot-starter
: springboot dubboçä¾èµã
å ¶ä»çå°±ä¸ä¸ä¸ä»ç»ï¼å ¶ä»çä¸ç®äºç¶ï¼å°±ç¥éæ¯å¹²ä»ä¹çã
3.8.2 application.propertiesé ç½®
server.port=8102
spring.application.name=dubbo-account-example
#====================================Dubbo config===============================================
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=zookeeper://127.0.0.1:2181
dubbo.protocol.port=20880
dubbo.application.qos-enable=false
dubbo.config-center.address=zookeeper://127.0.0.1:2181
dubbo.metadata-report.address=zookeeper://127.0.0.1:2181
#====================================mysql config============================================
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=123456
#=====================================mybatis config======================================
mybatis.mapper-locations=classpath*:/mapper/*.xml
3.8.3 registry.confï¼zkçé ç½®ï¼
registry.confçé ç½®
registry {
# file ãnacos ãeurekaãredisãzk
type = "zk"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:1001/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "127.0.0.1:6379"
db = "0"
}
zk {
cluster = "default"
serverAddr = "localhost:2181"
session.timeout = 6000
connect.timeout = 2000
}
file {
name = "file.conf"
}
}
config {
# fileãnacos ãapolloãzk
type = "zk"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
apollo {
app.id = "fescar-server"
apollo.meta = "http://192.168.1.204:8801"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
file {
name = "file.conf"
}
}
3.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");
}
}
å ¶ä¸:
@Bean
public GlobalTransactionScanner globalTransactionScanner(){
return new GlobalTransactionScanner("account-gts-seata-example", "account-service-seata-service-group");
}
GlobalTransactionScanner
: åå§åå
¨å±çäºå¡æ«æå¨
/**
* 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);
}
- applicationId ï¼ä¸ºåºç¨id è¿éæä¼ å
¥çæ¯
account-gts-seata-example
- txServiceGroup: é»è®¤serverçåç» è¿éæä¼ å
¥çæ¯
account-service-seata-service-group
è¿ä¸ªåæ们åé¢å¨nacos é ç½®çæ¯ä¿åä¸è´ã - DEFAULT_MODEï¼é»è®¤çäºå¡æ¨¡å¼ 为AT_MODE + MT_MODE
3.8.6 AccountExampleApplication å¯å¨ç±»çé ç½®
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);
}
}
-
@EnableDubbo
çåäº@DubboComponentScan
å@EnableDubboConfig
ç»å -
@DubboComponentScan
æ«æ classpaths ä¸ç±»ä¸æ·»å äº@Service
å@Reference
å°èªå¨æ³¨å ¥å°spring beansä¸ã -
@EnableDubboConfig æ«æçdubboçå¤é¨åé ç½®ã
4 å¯å¨ææçsample模å
å¯å¨ samples-account
ãsamples-order
ãsamples-storage
ãsamples-business
并ä¸å¨zkç客æ·ç«¯æ¥ç注åæ åµ
æ们å¯ä»¥çå°ä¸é¢çæå¡é½å·²ç»æ³¨åæåã
5 æµè¯
5. 1 åéä¸ä¸ªä¸å请æ±
使ç¨postman åé ï¼http://localhost:8104/business/dubbo/buy
åæ°ï¼
{
"userId":"1",
"commodityCode":"C201901140001",
"name":"fan",
"count":50,
"amount":"100"
}
è¿å
{
"status": 200,
"message": "æå",
"data": null
}
è¿æ¶åæ§å¶å°ï¼
2019-10-21 12:04:54.816 INFO 12780 --- [nio-8104-exec-1] i.s.s.i.c.controller.BusinessController : 请æ±åæ°ï¼BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)
2019-10-21 12:04:54.823 INFO 12780 --- [nio-8104-exec-1] i.s.common.loader.EnhancedServiceLoader : load ContextCore[null] extension by class[io.seata.core.context.ThreadLocalContextCore]
2019-10-21 12:04:54.829 ERROR 12780 --- [nio-8104-exec-1] i.s.config.zk.ZookeeperConfiguration : getConfig client.tm.commit.retry.count is error or timeout,return defaultValue 1
2019-10-21 12:04:54.830 ERROR 12780 --- [nio-8104-exec-1] i.s.config.zk.ZookeeperConfiguration : getConfig client.tm.rollback.retry.count is error or timeout,return defaultValue 1
2019-10-21 12:04:54.833 INFO 12780 --- [nio-8104-exec-1] i.s.common.loader.EnhancedServiceLoader : load TransactionManager[null] extension by class[io.seata.tm.DefaultTransactionManager]
2019-10-21 12:04:54.833 INFO 12780 --- [nio-8104-exec-1] io.seata.tm.TransactionManagerHolder : TransactionManager Singleton io.seata.tm.DefaultTransactionManager@394cfa5a
2019-10-21 12:04:54.840 INFO 12780 --- [nio-8104-exec-1] i.s.common.loader.EnhancedServiceLoader : load LoadBalance[null] extension by class[io.seata.discovery.loadbalance.RandomLoadBalance]
2019-10-21 12:04:55.037 INFO 12780 --- [nio-8104-exec-1] i.seata.tm.api.DefaultGlobalTransaction : Begin new global transaction [192.168.10.108:8091:2025358030]
2019-10-21 12:04:57.473 INFO 12780 --- [nio-8104-exec-1] i.s.s.i.c.service.BusinessServiceImpl : å¼å§å
¨å±äºå¡ï¼XID = 192.168.10.108:8091:2025358030
2019-10-21 12:05:04.280 INFO 12780 --- [nio-8104-exec-1] i.seata.tm.api.DefaultGlobalTransaction : [192.168.10.108:8091:2025358030] commit status:Committed
äºå¡æ交æåï¼
æ们æ¥çä¸ä¸æ°æ®åºæ°æ®åå
t_account
t_order
t_storage
æ°æ®æ²¡æé®é¢ã
5.2 æµè¯åæ»
æ们samples-business
å°BusinessServiceImpl
çhandleBusiness2
ä¸é¢ç代ç å»æ注é
if (!flag) {
throw new RuntimeException("æµè¯æå¼å¸¸åï¼åå¸å¼äºå¡åæ»ï¼");
}
使ç¨postman åé ï¼http://localhost:8104/business/dubbo/buy2
.ååºç»æï¼
{
"timestamp": "2019-09-05T04:29:34.178+0000",
"status": 500,
"error": "Internal Server Error",
"message": "æµè¯æå¼å¸¸åï¼åå¸å¼äºå¡åæ»ï¼",
"path": "/business/dubbo/buy"
}
æ§å¶å°
2019-10-21 12:05:46.752 INFO 12780 --- [nio-8104-exec-3] i.s.s.i.c.controller.BusinessController : 请æ±åæ°ï¼BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)
2019-10-21 12:05:46.785 INFO 12780 --- [nio-8104-exec-3] i.seata.tm.api.DefaultGlobalTransaction : Begin new global transaction [192.168.10.108:8091:2025358056]
2019-10-21 12:05:46.786 INFO 12780 --- [nio-8104-exec-3] i.s.s.i.c.service.BusinessServiceImpl : å¼å§å
¨å±äºå¡ï¼XID = 192.168.10.108:8091:2025358056
2019-10-21 12:05:50.285 INFO 12780 --- [nio-8104-exec-3] i.seata.tm.api.DefaultGlobalTransaction : [192.168.10.108:8091:2025358056] rollback status:Rollbacked
2019-10-21 12:05:50.477 ERROR 12780 --- [nio-8104-exec-3] 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: æµè¯æå¼å¸¸åï¼åå¸å¼äºå¡åæ»ï¼] with root cause
java.lang.RuntimeException: æµè¯æå¼å¸¸åï¼åå¸å¼äºå¡åæ»ï¼
at io.seata.samples.integration.call.service.BusinessServiceImpl.handleBusiness2(BusinessServiceImpl.java:93) ~[classes/:na]
at io.seata.samples.integration.call.service.BusinessServiceImpl$$FastClassBySpringCGLIB$$2ab3d645.invoke(<generated>) ~[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.9.0.jar:0.9.0]
at io.seata.tm.api.TransactionalTemplate.execute(TransactionalTemplate.java:64) ~[seata-all-0.9.0.jar:0.9.0]
at io.seata.spring.annotation.GlobalTransactionalInterceptor.handleGlobalTransaction(GlobalTransactionalInterceptor.java:101) ~[seata-all-0.9.0.jar:0.9.0]
at io.seata.spring.annotation.GlobalTransactionalInterceptor.invoke(GlobalTransactionalInterceptor.java:76) ~[seata-all-0.9.0.jar:0.9.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$$a8aa15d5.handleBusiness2(<generated>) ~[classes/:na]
at io.seata.samples.integration.call.controller.BusinessController.handleBusiness2(BusinessController.java:48) ~[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]
æ们æ¥çæ°æ®åºæ°æ®ï¼å·²ç»åæ»ï¼åä¸é¢çæ°æ®ä¸è´ã
å°è¿éä¸ä¸ªç®åçSpringBoot+Zookeeper+Seataå®ç°Dubboåå¸å¼äºå¡ç®¡ç
æ¡ä¾åºæ¬å°±åæç»æãæè°¢ä½ çå¦ä¹ ã
æåæ³ä¸èµ·äº¤æµææ¯çå¯ä»¥å æwx: