天天看点

Docker 部署 Seata Server(使用nacos 做为注册中心和配置中心)

组件版本关系

​​版本说明​​

每个 Spring Cloud Alibaba 版本及其自身所适配的各组件对应版本(经过验证,自行搭配各组件版本不保证可用)如下表所示(最新版本用*标记):

Docker 部署 Seata Server(使用nacos 做为注册中心和配置中心)

docker 拉取 seata镜像

​​seata github地址​​ seata目前已经维护到了1.4.2

​​seata 手动下载地址​​

Docker 部署 Seata Server(使用nacos 做为注册中心和配置中心)

因为和其他组件适配的关系,我使用docker部署1.3.0的seata

docker pull seataio/seata-server:1.3.0      

创建挂载文件夹

mkdir -p /usr/local/seata/config      

赋予权限

chmod -R 777 /usr/local/seata      

​​seata 配置的脚本文件地址​​ 放入了一系列的配置文件的脚本文件,大家可以自行查阅,也可以直接将seata的整个包下载下来

Docker 部署 Seata Server(使用nacos 做为注册中心和配置中心)

使用自定义配置文件

自定义配置文件需要通过挂载文件的方式实现,将宿主机上的 registry.conf 和 file.conf 挂载到容器中相应的目录(本博客只用挂载registry.conf 文件,file.conf文件的内容保存到nacos-server里面)

registry.conf

用于指定注册中心和配置中心的配置文件。如果是单机的seata server,采用注册中心和配置中心都采用file模式比较好,意思就是这些信息都会保存在本地,性能是比较好的。但如果要部署seata server集群,就引入其他组件来做注册中心和配置中心会比较方便。

如果需要同时指定 file.conf 配置文件,则需要在 registry.conf 文件中将 config 配置改为以下内容,name 的值为容器中对应的路径。从本地读取配置信息。

配置信息也可以放在配置中心上面(本博客是部署到nacos上面,这里只是做一个介绍,配置文件不用像下面这么指定,因为不会使用本地文件)

config {
  type = "file"

  file {
    name = "file:/root/seata-config/file.conf"
  }
}      

文件如下所示,注册中心和配置中心都使用nacos

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"
  loadBalance = "RandomLoadBalance"

  nacos {
    application = "seata-server"
    serverAddr = "IP:8848"
    group = "SEATA_GROUP"
    namespace = ""   # 用来做资源隔离的
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
    password = ""
    timeout = "0"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file:/root/seata-config/file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
  type = "nacos"

  nacos {
    serverAddr = "IP:8848"
    namespace = ""
    group = "SEATA_GROUP"
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    appId = "seata-server"
    apolloMeta = "http://192.168.1.204:8801"
    namespace = "application"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file:/root/seata-config/file.conf"
  }
}      

file.conf 配置文件

用于指定数据存储的配置文件(当我们把配置信息写入到nacos的时候,这个本地的配置文件已经不需要了,不过这里还是简单介绍一下)

因为TC需要进行全局事务和分支事务的记录,所以需要对应的存储。

Server端存储模式(store.mode) 支持三种:

  • file:单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高(默认)
  • db:(mysql 需要5.7以上的版本,我的mysql是部署的8)高可用模式,全局事务会话信息通过db共享,相应性能差些
  • redis: Seata-Server 1.3及以上版本支持,性能较高,存在事务信息丢失风险,请提前配置适合当前场景的redis持久化配置

我这里采用db存储,自己修改mysql 的连接信息

## transaction log store, only used in seata-server
store {
  ## store mode: file、db、redis
  mode = "db"

  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }

  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://IP:3306/seata_server"
    user = "mysql"
    password = "mysql"
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }

  ## redis store property
  redis {
    host = "127.0.0.1"
    port = "6379"
    password = ""
    database = "0"
    minConn = 1
    maxConn = 10
    queryLimit = 100
  }
}      

mysql 部署

建库seata_server ,用来保存事务信息

CREATE DATABASE seata_server;      

​​sql脚本地址​​mysql.sql 建表的sql 文件

有三个表

  • global_table(全局事务): 每当有一个全局事务发起后,就会在该表中记录全局事务的ID和信息。
  • branch_table(分支事务): 记录每一个分支事务的ID,分支事务操作的哪个数据库等信息
  • lock_table(全局锁):用于申请全局锁。存储锁的相关信息,比如锁的是那张表等等
-- -------------------------------- 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_status_gmt_modified` (`status` , `gmt_modified`),
    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(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_status` (`status`),
    KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

CREATE TABLE IF NOT EXISTS `distributed_lock`
(
    `lock_key`       CHAR(20) NOT NULL,
    `lock_value`     VARCHAR(20) NOT NULL,
    `expire`         BIGINT,
    primary key (`lock_key`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('HandleAllSession', ' ', 0);      

使用nacos 保存 seata server的配置信息

​​示例配置文件地址​​​ 所有的配置信息都在这个地址的config.txt文件里面,都是默认配置

我们针对我们的设置做一点修改

store.mode默认为file,我这里修改成db。自己的mysql的相关信息自己修改

store.mode:把store.mode=file 改为 store.mode=db 代表采用数据库存储Seata-Server的全局事务数据

store.db.driverClassName:驱动根据mysql版本做相应修改,8.0以上用com.mysql.cj.jdbc.Driver,8.0以下用com.mysql.jdbc.Driver

store.db.url:数据库连接地址
store.db.user:数据库用户名
store.db.password:数据库密码      

我的mysql是8,所以

store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://IP:3306/seata_server?useUnicode=true
store.db.user=username
store.db.password=password
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      

配置事务分组的信息:是为了异地容灾而出现的,集群里面有多个事务分组,分布在不同的城市

service.vgroupMapping.changsha_tx_group=default 要和registry.conf配置文件的cluster对应,这里为default

service.default.grouplist=127.0.0.1:8091 当注册中心为file的时候,这个配置才有用,因为这个时候会读取配置文件得知seata-server服务的地址在那

当注册中心为其他组件时,他会从注册中心得知seata-server服务的地址,而非从配置文件得知(这个是我结合实践猜想的,如果大家有不同见解,请大家指教)

service.vgroupMapping.changsha_tx_group=default
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false      

使用脚本文件nacos-config.sh 将本地配置文件推送到nacos上面

#!/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

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=$1&group=$group&content=$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   #读取上一级文件夹里面的config.txt 文件
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      

注意:仔细看脚本里面的代码,是读取上一级别文件路径下的config.txt文件,然后写入nacos-server, 是写死的 。

Docker 部署 Seata Server(使用nacos 做为注册中心和配置中心)

所以我们要把这个包下载下来

Docker 部署 Seata Server(使用nacos 做为注册中心和配置中心)

找一个能执行脚本文件的终端,我这里使用git的终端

sh nacos-config.sh -h 81.68.82.48 -p 8848 -g SEATA_GROUP -t xxx      

参数说明:

-h: host,默认值localhost

-p: port,默认值8848

-g:配置分组,默认值为’SEATA_GROUP’

-t:租户信息,对应Nacos的命名空间ID字段,默认值为空

访问nacos-server 的UI页面,已经可以看见配置信息已经写入nacos了

Docker 部署 Seata Server(使用nacos 做为注册中心和配置中心)

环境变量

seata-server 支持以下环境变量:

  • SEATA_IP: 可选, 指定seata-server启动的IP, 该IP用于向注册中心注册时使用, 如eureka等
  • SEATA_PORT: 可选, 指定seata-server启动的端口, 默认为 8091
  • STORE_MODE: 可选, 指定seata-server的事务日志存储方式, 支持db ,file,redis(Seata-Server 1.3及以上版本支持), 默认是 file
  • SERVER_NODE: 可选, 用于指定seata-server节点ID, 如 1,2,3…, 默认为 根据ip生成
  • SEATA_ENV: 可选, 指定 seata-server 运行环境, 如 dev, test 等, 服务启动时会使用 registry-dev.conf 这样的配置
  • SEATA_CONFIG_NAME: 可选, 指定配置文件位置, 如 file:/root/registry, 将会加载 /root/registry.conf 作为配置文件,如果需要同时指定 file.conf文件,需要将registry.conf的config.file.name的值改为类似file:/root/file.conf:

创建运行容器

将registry.conf 配置文件通过xftp软件移动到宿主机的挂载文件夹下

使用自定义配置文件时必须指定环境变量 SEATA_CONFIG_NAME, 并且值需要以file:开始, 如: file:/root/seata-config/registry

docker run -itd --name seata-server \
        -p 8091:8091 \
        -e SEATA_CONFIG_NAME=file:/root/seata-config/registry \
        -e SEATA_IP=宿主机IP \
        -v /usr/local/seata/config:/root/seata-config  \
        seataio/seata-server:1.3.0      

其中 -e 用于配置环境变量, -v 用于挂载宿主机的目录

使用nacos 做为注册中心,还是指定宿主机的IP比较好,不然就会出现这篇博客的情况

​spring cloud 集成seata的时候:can not register RM,err:can not connect to services-server.​​

容器命令行及查看日志

docker exec -it seata-server sh      
docker logs -f seata-server      
Docker 部署 Seata Server(使用nacos 做为注册中心和配置中心)

对外开放端口

firewall-cmd --permanent --add-port=8091/tcp      

重启防火墙(修改配置后要重启防火墙)

firewall-cmd --reload      

还需要去服务器的控制台去开启防火墙,

如果是阿里云,还需要在安全组里面手动添加端口 ,配置可由那些IP访问。

查看nacos 的注册服务列表

References:

  • ​​https://seata.io/zh-cn/docs/ops/deploy-by-docker.html​​
  • ​​https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E​​