天天看点

seata1.4.0落地

1,简介

        由于项目功能已经开发完成,但是某些功能牵扯到的分布式事务没有处理,而在调研了一些分布式解决方案后,最终选择了当前比较流行,且对现有业务代码无侵入的seata。

        本项目情况:

        spring boot 2.0.9.RELEASE + sprong cloudFinchley.SR3 + nacos1.3.2作为注册中心+ oracle12C

        本次集成seata 1.4.0 : 需要单独部署TC(seata-server服务)

为防止部分资源无法下载,文末会附上相关下载资源

2,seata服务端安装

        仔细官方文档,很重要,能避免后期一些坑。

        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压缩

seata1.4.0落地

如果没有logs文件夹,则创建一个logs文件夹,并在logs文件夹里创建一个seata_gc.log文件

2.4打开目录seata/cof在README-zh.md中有一些资源github下载地址

seata1.4.0落地

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

以下是需要自行更改的内容,包含数据库连接信息,自行修改

seata1.4.0落地

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注册中心类型及注册中西配置

seata1.4.0落地

2,配置中心类型及相关参数

seata1.4.0落地

2.7.4修改项目目录下的file.conf文件

更改数据库信息,如有redis更改redis相关配置

seata1.4.0落地

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

seata1.4.0落地
seata1.4.0落地
seata1.4.0落地

2.9 启动seata服务,在seataa/bin 文件夹下执行,windows的话执行.bat  linux执行.sh文件

seata1.4.0落地

如下表示启动成功,可取nacos里去查看

seata1.4.0落地
seata1.4.0落地
seata1.4.0落地

至此TC(服务端)配置启动完成

2.9客户端配置(自己的服务)

2.9.1 引入依赖,

注意引入依赖这一步可能会有些模范,首先官网提供了依赖版本说明

https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html

可参考官网提供的版本配置方式。

可能我项目内spring boot cloud版本比较老所有几番尝试之后使用了如下配置:

主项目:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.0.4.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
           

子项目:

<dependency>
   <groupId>io.seata</groupId>
   <artifactId>seata-spring-boot-starter</artifactId>
   <version>1.4.0</version>
</dependency>
           
seata1.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
           
seata1.4.0落地
seata1.4.0落地

2.10,业务代码增加配置

@GlobalTransactional(rollbackFor = Exception.class)
           
seata1.4.0落地

注意:完成上述工作后,因为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();
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
                Map<String, Collection<String>> resolvedHeaders = new CaseInsensitiveKeyMap<>();
                resolvedHeaders.putAll(template.headers());

                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    if (!resolvedHeaders.containsKey(name)) {
                        String values = request.getHeader(name);
                        List<String> headers = new ArrayList<String>();
                        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处添加此配置类就可以      
seata1.4.0落地

至此客户端也配置完成。  一定要自己查看官方文档,避免踩空

过程中可能出现问题:

因为本人已经踩过很多坑,给出的配置文件基本上已经是更改过的,此前出现过的一些报错信息及解决记录:

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配置文件里

seata1.4.0落地
seata1.4.0落地

关于这个报错  还需要注意:config.txt里的service.vgroupMapping.mjkf-agility-sys-manage-group=default(注:mjkf-agility-sys-manage-group 为自定义)这个配置即为nacos里的配置

seata1.4.0落地

与yml文件里的这两处一定要保持一致,否在会此错误。

seata1.4.0落地

-----------------------------------------------------------------------------------------

相关资源:

https://download.csdn.net/download/u010185947/20065800