seata分为服务端和客户端,分别来介绍是怎么启动的。
1、下载启动包:https://github.com/seata/seata/releases/download/v1.2.0/seata-server-1.2.0.zip
2、修改conf/registry.conf
registry.conf中分为两部分,一部分是注册中心,一部分为配置中心,在这里注册中心和配置中心我选择的都是nacos。
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "serverAddr"
serverAddr = "127.0.0.1:8848"
namespace = "public"
cluster = "default"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = "public"
cluster = "default"
}
}
为何将application 设置为serverAddr?
在我目前使用的seata版本中(目前使用的版本为0.7,换成其他版本应该不需要配置,默认是seata-server,但是具体还是需要看一下源码),NacosRegistryServiceImpl(因为我选择的是nacos作为注册中心)中有一个属性PRO_SERVER_ADDR_KEY,默认的值为serverAddr,seata client会定时的以serverAddr作为服务名称向nacos拉取列表,所以将application 设置为serverAddr。拉取服务列表的代码如下:
@Override
public List lookup(String key) throws Exception {
Configuration config = ConfigurationFactory.getInstance();
String clusterName = config.getConfig(PREFIX_SERVICE_ROOT + CONFIG_SPLIT_CHAR + PREFIX_SERVICE_MAPPING + key);
if (null == clusterName) {
return null;
}
if (!LISTENER_SERVICE_MAP.containsKey(clusterName)) {
List clusters = new ArrayList<>();
clusters.add(clusterName);
// 向nacos拉取服务列表
List firstAllInstances = getNamingInstance().getAllInstances(PRO_SERVER_ADDR_KEY, clusters);
if (null != firstAllInstances) {
List newAddressList = new ArrayList<>();
for (Instance instance : firstAllInstances) {
if (instance.isEnabled() && instance.isHealthy()) {
newAddressList.add(new InetSocketAddress(instance.getIp(), instance.getPort()));
}
}
CLUSTER_ADDRESS_MAP.put(clusterName, newAddressList);
}
subscribe(clusterName, new EventListener() {
@Override
public void onEvent(Event event) {
List instances = ((NamingEvent) event).getInstances();
if (null == instances && null != CLUSTER_ADDRESS_MAP.get(clusterName)) {
CLUSTER_ADDRESS_MAP.remove(clusterName);
} else if (!CollectionUtils.isEmpty(instances)) {
List newAddressList = new ArrayList<>();
for (Instance instance : instances) {
if (instance.isEnabled() && instance.isHealthy()) {
newAddressList.add(new InetSocketAddress(instance.getIp(), instance.getPort()));
}
}
CLUSTER_ADDRESS_MAP.put(clusterName, newAddressList);
}
}
});
}
return CLUSTER_ADDRESS_MAP.get(clusterName);
}
3、将配置信息上传至nacos。在https://github.com/seata/seata/tree/1.2.0/script/config-center中一个文件config.txt,将这个文件下载下来,修改一下其中的信息,主要修改一下几个配置:
(1)vgroup_mapping后面的是自己定义的,不过要和seata client端配置的spring.cloud.alibaba.seata.tx-service-group要一样即可,有几个client就配置几个,此处我只有两个client端,就配置了两个。
service.vgroup_mapping.order-service-group=default
service.vgroup_mapping.storage-service-group=default
(2)数据源信息的修改:
(3)上传至nacos,利用https://github.com/seata/seata/tree/1.2.0/script/config-center/nacos 下的nacos-config.sh上传。
sh nacos-config.sh 127.0.0.1(nacos的ip地址)
下面是我的配置文件:
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
service.vgroup_mapping.order-service-group=default
service.vgroup_mapping.storage-service-group=default
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=false
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
store.mode=file
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100
store.db.datasource=druid
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=1111
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
4、创建三张表。
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
5、启动nacos
6、启动seata-server,点击bin目录下的seata-server.bat。看到以下信息说明启动成功了。
到此为止server端已经配置完毕,接下来开始client端的配置。
1、添加依赖。我的项目有父项目和子项目。
父项目的依赖如下:
org.springframework.boot
spring-boot-starter-parent
2.2.2.RELEASE
pom
2.1.0.RELEASE
3.1.2
Finchley.RELEASE
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${springcloud.alibaba.version}
pom
import
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus-boot-starter.version}
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
子项目的依赖如下:
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.boot
spring-boot-starter-web
com.alibaba.cloud
spring-cloud-alibaba-seata
mysql
mysql-connector-java
runtime
com.baomidou
mybatis-plus-boot-starter
org.projectlombok
lombok
org.springframework.cloud
spring-cloud-starter-openfeign
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
2、配置。
(1)配置配置中心,在bootstrap.yml中配置。
spring:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
(2)nacos注册中心,数据源以及其他配置。
server:
port: 8888
spring:
cloud:
nacos:
discovery:
# server-addr: 127.0.0.1:8848,127.0.0.1:8847 多个nacos服务用逗号隔开
server-addr: 127.0.0.1:8848
alibaba:
seata:
tx-service-group: order-service-group # seata 服务分组,要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应
application:
name: nacos-provider #名称需要配置,不然不会注册
datasource:
url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 1111
type: com.alibaba.druid.pool.DruidDataSource
#logging:
# level:
# io.seata: debug
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
mapper-locations: mappers/*.xml
3、将registry.conf文件拷贝到根目录下,就是server端的registry.conf。
4、创建业务表和undo_log 表
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT '0',
`money` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=62 DEFAULT CHARSET=utf8;
CREATE TABLE `storage_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `undo_log` (
`branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
`xid` varchar(100) NOT NULL COMMENT 'global transaction id',
`context` varchar(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` longblob NOT NULL COMMENT 'rollback info',
`log_status` int(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` datetime(6) NOT NULL COMMENT 'create datetime',
`log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='AT transaction mode undo table';
5、将数据源修改为DataSourceProxy。
@Configuration
public class DataSourceProxyConfig implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource) {
return new DataSourceProxy((DataSource)bean);
}
return bean;
}
}
如果有@EnableAutoDataSourceProxy注解也可以加上这个注解
6、在业务方法上添加@GlobalTransactional注解。
@GlobalTransactional
@Transactional(rollbackFor = Exception.class)
public void placeOrder(String userId, String commodityCode, Integer count) {
BigDecimal orderMoney = new BigDecimal(count).multiply(new BigDecimal(5));
Order order = new Order();
order .setUserId(userId);
order.setCommodityCode(commodityCode);
order.setCount(count);
order.setMoney(orderMoney);
orderMapper.insert(order);
storageFeignClient.deduct(commodityCode, count);
}
7、创建undo_logo,如果每个client端一个数据库,那么要在所有的数据库中创建这张表,否则不会回滚。
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
8、启动即可。
客户端启动信息如下:
服务端信息如下:
还有一个仓储服务,和上面的配置一样
出现以下问题:说明maven版本太低,需要下载版本3.6.0版本的maven,然后修改IDEA的maven配置
客户端连接不上服务端,确保两端的ip是在同一局域网内。
比如客户端注册的ip地址为192.168.228.1,服务端注册的为192.168.92.1,这样两端的ip地址不在同一网段内,也会连接不上