由于项目功能已经开发完成,但是某些功能牵扯到的分布式事务没有处理,而在调研了一些分布式解决方案后,最终选择了当前比较流行,且对现有业务代码无侵入的seata。
本项目情况:
spring boot 2.0.9.RELEASE + sprong cloudFinchley.SR3 + nacos1.3.2作为注册中心+ oracle12C
本次集成seata 1.4.0 : 需要单独部署TC(seata-server服务)
为防止部分资源无法下载,文末会附上相关下载资源
仔细官方文档,很重要,能避免后期一些坑。
2.1 seata官方文档路径:https://seata.io/zh-cn/index.html
2.2 seata下载路径:http://seata.io/zh-cn/blog/download.html
2.3下载好1.4.0压缩
如果没有logs文件夹,则创建一个logs文件夹,并在logs文件夹里创建一个seata_gc.log文件
2.4打开目录seata/cof在README-zh.md中有一些资源github下载地址
2.5创建seata-server 数据库
本项目只有一个数据库所有在该项目的数据库里创建一个用户,作为seata的数据库,用户名叫seata
脚本:
CREATE TABLE global_table
(
xid VARCHAR2(128) NOT NULL,
transaction_id NUMBER(19),
status NUMBER(3) NOT NULL,
application_id VARCHAR2(32),
transaction_service_group VARCHAR2(32),
transaction_name VARCHAR2(128),
timeout NUMBER(10),
begin_time NUMBER(19),
application_data VARCHAR2(2000),
gmt_create TIMESTAMP(0),
gmt_modified TIMESTAMP(0),
PRIMARY KEY (xid)
);
CREATE INDEX idx_gmt_modified_status ON global_table (gmt_modified, status);
CREATE INDEX idx_transaction_id ON global_table (transaction_id);
-- the table to store BranchSession data
CREATE TABLE branch_table
(
branch_id NUMBER(19) NOT NULL,
xid VARCHAR2(128) NOT NULL,
transaction_id NUMBER(19),
resource_group_id VARCHAR2(32),
resource_id VARCHAR2(256),
branch_type VARCHAR2(8),
status NUMBER(3),
client_id VARCHAR2(64),
application_data VARCHAR2(2000),
gmt_create TIMESTAMP(6),
gmt_modified TIMESTAMP(6),
PRIMARY KEY (branch_id)
);
CREATE INDEX idx_xid ON branch_table (xid);
-- the table to store lock data
CREATE TABLE lock_table
(
row_key VARCHAR2(128) NOT NULL,
xid VARCHAR2(128),
transaction_id NUMBER(19),
branch_id NUMBER(19) NOT NULL,
resource_id VARCHAR2(256),
table_name VARCHAR2(32),
pk VARCHAR2(36),
gmt_create TIMESTAMP(0),
gmt_modified TIMESTAMP(0),
PRIMARY KEY (row_key)
);
CREATE INDEX idx_branch_id ON lock_table (branch_id);
2.6创建undo_log表
说明:该表在seata服务数据库创建一张,在相关业务库也都要创建,如果是多个业务库,每个业务库都要创建。
-- Create table
create table UNDO_LOG
(
id NUMBER(19) not null,
branch_id NUMBER(19) not null,
xid VARCHAR2(128) not null,
context VARCHAR2(128) not null,
rollback_info BLOB not null,
log_status NUMBER(10) not null,
log_created TIMESTAMP(0) not null,
log_modified TIMESTAMP(0) not null
);
comment on table UNDO_LOG
is 'AT transaction mode undo table';
2.7 更改seata服务相关配置文件
2.7.1在根目录seata下创建config.txt文件
service.vgroupMapping.mjkf-agility-sys-manage_tx_group=default
service.vgroup_mapping.sys_tx_group=default
service.vgroup_mapping.mjkf-agility-sys-manage-htf-group=default
service.vgroupMapping.mjkf-agility-sys-manage-htf-group=default
service.vgroupMapping.sys_tx_group=default
service.vgroupMapping.sys_group=default
service.vgroupMapping.mjkf-agility-dbmodel_tx_group=default
service.vgroupMapping.mjkf-agility-code_tx_group=default
service.default.grouplist=10.1.5.98:8091
service.disableGlobalTransaction=false
store.mode=db
store.db.datasource=druid
store.db.dbType=oracle
store.db.driverClassName=oracle.jdbc.OracleDriver
store.db.url=jdbc:oracle:thin:@10.1.5.98:1521/bxpdb
store.db.user=seata
store.db.password=seata
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
以下是需要自行更改的内容,包含数据库连接信息,自行修改
2.7.2在seata/conf/文件夹下增加nacos-config.sh 文件 内容如下:
#!/usr/bin/env bash
# Copyright 1999-2019 Seata.io Group.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at、
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
while getopts ":h:p:g:t:u:w:" opt
do
case $opt in
h)
host=$OPTARG
;;
p)
port=$OPTARG
;;
g)
group=$OPTARG
;;
t)
tenant=$OPTARG
;;
u)
username=$OPTARG
;;
w)
password=$OPTARG
;;
?)
echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] "
exit 1
;;
esac
done
urlencode() {
for ((i=0; i < ${#1}; i++))
do
char="${1:$i:1}"
case $char in
[a-zA-Z0-9.~_-]) printf $char ;;
*) printf '%%%02X' "'$char" ;;
esac
done
}
if [[ -z ${host} ]]; then
host=localhost
fi
if [[ -z ${port} ]]; then
port=8848
fi
if [[ -z ${group} ]]; then
group="SEATA_GROUP"
fi
if [[ -z ${tenant} ]]; then
tenant=""
fi
if [[ -z ${username} ]]; then
username=""
fi
if [[ -z ${password} ]]; then
password=""
fi
nacosAddr=$host:$port
contentType="content-type:application/json;charset=UTF-8"
echo "set nacosAddr=$nacosAddr"
echo "set group=$group"
failCount=0
tempLog=$(mktemp -u)
function addConfig() {
curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$(urlencode $1)&group=$group&content=$(urlencode $2)&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null
if [[ -z $(cat "${tempLog}") ]]; then
echo " Please check the cluster status. "
exit 1
fi
if [[ $(cat "${tempLog}") =~ "true" ]]; then
echo "Set $1=$2 successfully "
else
echo "Set $1=$2 failure "
(( failCount++ ))
fi
}
count=0
for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do
(( count++ ))
key=${line%%=*}
value=${line#*=}
addConfig "${key}" "${value}"
done
echo "========================================================================="
echo " Complete initialization parameters, total-count:$count , failure-count:$failCount "
echo "========================================================================="
if [[ ${failCount} -eq 0 ]]; then
echo " Init nacos config finished, please start seata-server. "
else
echo " init nacos config fail. "
fi
2.7.3更改seata/cong文件夹下的registry.conf,修改两处
1注册中心类型及注册中西配置
2,配置中心类型及相关参数
2.7.4修改项目目录下的file.conf文件
更改数据库信息,如有redis更改redis相关配置
2.8将fonfig.txt上传至nacos
在seata/conf文件夹下执行如下上传命令:
sh nacos-config.sh -h 10.1.5.88 -p 8848 -g SEATA_GROUP -t 60e39b35-0229-4733-addf-fa9eedcd8e33 -u admin -w 123456
2.9 启动seata服务,在seataa/bin 文件夹下执行,windows的话执行.bat linux执行.sh文件
如下表示启动成功,可取nacos里去查看
至此TC(服务端)配置启动完成
2.9客户端配置(自己的服务)
2.9.1 引入依赖,
注意引入依赖这一步可能会有些模范,首先官网提供了依赖版本说明
https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html
可参考官网提供的版本配置方式。
可能我项目内spring boot cloud版本比较老所有几番尝试之后使用了如下配置:
主项目:
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.0.4.RELEASE
pom
import
子项目:
io.seata
seata-spring-boot-starter
1.4.0
2.9.2yml配置,增加如下配置
seata:
enabled: true
application-id: mjkf-agility-sys-manage-htf
tx-service-group: mjkf-agility-sys-manage-group
data-source-proxy-mode: AT
use-jdk-proxy: true
registry:
type: nacos
nacos:
application: seata-server
server-addr: 10.1.5.88:8848
username: admin
password: 123456
config:
type: nacos
nacos:
server-addr: 10.1.5.88:8848
group: SEATA_GROUP
username: admin
password: 123456
namespace: 60e39b35-0229-4733-addf-fa9eedcd8e33
#data-id: seata.properties
#namespace: 5342dc77-dbb7-4ac7-ad72-9f65b71b1e94
service:
vgroupMapping:
mjkf-agility-sys-manage-group: default
client:
rm:
report-success-enable: false
grouplist:
default: 10.1.5.98:8091
@GlobalTransactional(rollbackFor = Exception.class)
注意:完成上述工作后,因为fegin调用需要绑定XID的传递,以下附上绑定代码
新建一个配置类
import feign.RequestInterceptor; import feign.RequestTemplate; import io.seata.core.context.RootContext; import org.apache.commons.lang.StringUtils; import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.*; /** * @author htf * @description * @date 2021-7-12 10:56 */ @Configuration public class FeignSupportConfig implements RequestInterceptor { /** * 解决服务直接调用请求头不传递的问题 * @param template */ @Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes != null) { HttpServletRequest request = attributes.getRequest(); EnumerationheaderNames = request.getHeaderNames(); if (headerNames != null) { Map > resolvedHeaders = new CaseInsensitiveKeyMap<>(); resolvedHeaders.putAll(template.headers()); while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); if (!resolvedHeaders.containsKey(name)) { String values = request.getHeader(name); List headers = new ArrayList (); headers.addAll(Arrays.asList(values)); resolvedHeaders.put(name, headers); } } template.headers(null); template.headers(resolvedHeaders); } } String xid = RootContext.getXID(); if (StringUtils.isNotBlank(xid)) { template.header(RootContext.KEY_XID, xid); } } } 然后再feigin调用server处添加此配置类就可以
至此客户端也配置完成。 一定要自己查看官方文档,避免踩空
过程中可能出现问题:
因为本人已经踩过很多坑,给出的配置文件基本上已经是更改过的,此前出现过的一些报错信息及解决记录:
1,客户端服务一致起不起来 一致报错
Could not generate CGLIB subclass of class com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper:
Common causes of this problem include using a final class or a non-visible class;
nested exception is org.springframework.cglib.core.CodeGenerationException:
java.lang.reflect.InvocationTargetException-->null
解决:yml配置文件增加use-jdk-proxy: true 解决
2,seata服务端已启动,客户端还是一直报错:
no available service found in cluster ‘default’, please make sure registry config correct and keep your seata server running
或者
can not get cluster name in registry config 'service.vgroupMapping.my_test_tx_group', please make sure registry config correct
解决:注意在nacos的seata-server服务与 nacos的配置中心分组保持一致,都使用SEATA_GROUP
这个分组是在配置文件里 registry.conf配置文件里
关于这个报错 还需要注意:config.txt里的service.vgroupMapping.mjkf-agility-sys-manage-group=default(注:mjkf-agility-sys-manage-group 为自定义)这个配置即为nacos里的配置
与yml文件里的这两处一定要保持一致,否在会此错误。
-----------------------------------------------------------------------------------------
相关资源:
https://download.csdn.net/download/u010185947/20065800