当前位置:网站首页>Seata Practice record for Distributed transactions

Seata Practice record for Distributed transactions

2022-01-15 02:26:47 Danny... Idea

J'ai passé les deux dernières semaines à étudiertccQuelques bases connexes pour les transactions distribuées,Cette semaine, j'écris un article surseataArticles pratiques de.
En ligneseataAtterrissagedemoEn fait, c'est beaucoup.,J'a i marché sur un certain nombre de trous dans le processus d'atterrissage réel en combinant des cas et des articles connexes,Cet article se concentre donc sur les difficultés rencontrées dans les cas d'atterrissage.

Sélection technique

SpringBoot + Dubbo + JdbcTemplate + MySQL + Seata + Nacos

Utiliser le scénario

Lors de l'achat de la marchandise,Déduisez l'inventaire et insérez une donnée de commande en même temps.
ps:Simple simulation de scène,Aucune opération complexe liée au verrouillage des stocks n'a été effectuée,Juste pour vérifierseataCapable d'assurer l'efficacité des transactions distribuées dans un scénario Multi - bibliothèques

Photos: https://uploader.shimo.im/f/yGRJm6CpqGV1oSyv.png

ConstructionseataDéploiement autonome

Tout d'abord, assurez - vous que la construction est faite nacosEnvironnement etmysqlEnvironnement, Introduction à cette section .

mysqlConstruction de montres
ConstructionseataBase de données utilisée:seata
Et aprèsseata Créer trois tableaux dans la base de données :

CREATE TABLE `branch_table` (
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(128) COLLATE utf8_bin NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `resource_group_id` varchar(32) COLLATE utf8_bin DEFAULT NULL,
  `resource_id` varchar(256) COLLATE utf8_bin DEFAULT NULL,
  `lock_key` varchar(128) COLLATE utf8_bin DEFAULT NULL,
  `branch_type` varchar(8) COLLATE utf8_bin DEFAULT NULL,
  `status` tinyint(4) DEFAULT NULL,
  `client_id` varchar(64) COLLATE utf8_bin DEFAULT NULL,
  `application_data` varchar(2000) COLLATE utf8_bin DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

CREATE TABLE `global_table` (
  `xid` varchar(128) COLLATE utf8_bin NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `status` tinyint(4) NOT NULL,
  `application_id` varchar(32) COLLATE utf8_bin DEFAULT NULL,
  `transaction_service_group` varchar(32) COLLATE utf8_bin DEFAULT NULL,
  `transaction_name` varchar(128) COLLATE utf8_bin DEFAULT NULL,
  `timeout` int(11) DEFAULT NULL,
  `begin_time` bigint(20) DEFAULT NULL,
  `application_data` varchar(2000) COLLATE utf8_bin 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=utf8 COLLATE=utf8_bin;


CREATE TABLE `lock_table` (
  `row_key` varchar(128) COLLATE utf8_bin NOT NULL,
  `xid` varchar(96) COLLATE utf8_bin DEFAULT NULL,
  `transaction_id` mediumtext COLLATE utf8_bin,
  `branch_id` mediumtext COLLATE utf8_bin,
  `resource_id` varchar(256) COLLATE utf8_bin DEFAULT NULL,
  `table_name` varchar(32) COLLATE utf8_bin DEFAULT NULL,
  `pk` varchar(36) COLLATE utf8_bin DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`row_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

Photos: https://uploader.shimo.im/f/nlpAyxqT3ss5jM0p.png

Ensuite, la base de données des commandes et la base de données des produits sont établies respectivement. mall_orderEtmall_goods

mall_order Créer une table de commande simple dans la base de données

CREATE TABLE `t_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(60) COLLATE utf8_bin DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `goods_id` int(11) DEFAULT NULL,
  `stock` int(6) DEFAULT NULL COMMENT 'Inventaire',
  `unit` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT 'Unité',
  `status` tinyint(3) DEFAULT NULL COMMENT 'État de la commande 0 En attente,1Soumis avec succès,2Échec de la soumission',
  `valid_status` tinyint(3) DEFAULT NULL COMMENT ' Si la commande est valide  0Ça marche. 2Invalide',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 'Temps de création',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Mise à jour',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Tableau des commandes';

Inmall_order Créer un undo_logTableau

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=9 DEFAULT CHARSET=utf8;

mall_goods Créer une liste de produits simple dans la base de données

CREATE TABLE `t_goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `goods_name` varchar(30) COLLATE utf8_bin DEFAULT NULL,
  `stock` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Fiche d'information sur les produits';

Inmall_goods Créer un undo_logTableau

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=9 DEFAULT CHARSET=utf8;

Deux.undo_logC'est pourseata Spécialement utilisé pour , Il n'y a pas beaucoup de corrélation avec les processus opérationnels de base , On n'a pas à s'occuper de , Je vous présente .

Et puisseataInstallation
Études initialesseata, Le choix est de construire un environnement autonome pour le déploiement .
Télécharger à l'adresse officielle seata:
https://github.com/seata/seata/releases
J'utilise1.1.0Paquet d'installation de version,L'environnement d'installation estmac pro
https://github.com/seata/seata/releases/tag/v1.1.0

Insérer la description de l'image ici

Description de la structure du paquet d'installation
Après le téléchargement, les fichiers suivants seront affichés lors de l'extraction: :
Insérer la description de l'image ici
bin Sous le Répertoire se trouvent les principaux fichiers de démarrage
conf Voici les profils associés
config.txt C'est un profil que j'ai ajouté plus tard. , Ici, les lecteurs peuvent d'abord ignorer
lib Est le Répertoire dans lequel les paquets dépendants sont stockés

Ajustement du profil
Suivez les instructions du site officiel , J'arrive conf Sous le Répertoire, puis ouvrez le fichier de configuration associé :
/conf/register.conf

registry {
    
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  # seata Le registre de nacos
  type = "nacos"

  
  nacos {
    
    #Adresse enregistrée
    serverAddr = "localhost:8848"
    # Écrire vide par défaut 
    namespace = ""
    # La version autonome est construite ici pour écrire defaultC'est bon
    cluster = "default"


  }
  eureka {
    
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    
    name = "file.conf"
  }
}

config {
    
  # file、nacos 、apollo、zk、consul、etcd3
  #  Configurer le module typeÉcris aussinacos
  type = "nacos"

  #Même chose., C'est la même configuration ici 
  nacos {
    
    serverAddr = "localhost:8848"
    namespace = ""
  }
  consul {
    
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
    namespace = "application"
  }
  zk {
    
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    
    serverAddr = "http://localhost:2379"
  }
  file {
    
    name = "file.conf"
  }
}

Les principaux changements apportés à ce profil sont les suivants: config Sélection des modules à utiliser nacos,register Le module a également choisi d'utiliser nacos, Et vous devez définir ipPropriétés telles que l'adresse.

Et puis on en voit une copie config.txtDocumentation, Sinon, créez un nouveau fichier .
config.txtLes fichiers sont configurés comme suit:

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.vgroupMapping.my_tcc=default #Le format par défaut estservice.vgroupMapping Ajouter un nom de groupe de transaction personnalisé , Je m'appelle my_tcc,Puis assignez la valeur àdefault

service.default.grouplist=127.0.0.1:8091 #Écris ici.nacosAdresse de configuration pour
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.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.support.spring.datasource.autoproxy=true 
store.mode=db # La valeur par défaut ici est fileMode,Doit maintenant être remplacé pardbMode
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=dbcp # La valeur par défaut ici est d'utiliser druidPool de connexion de données, Je vais utiliser dbcp
store.db.dbType=mysql 
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://prod.min.mall.com:3306/seata?useUnicode=true
store.db.user=youruser # Nom du compte de base de données 
store.db.password=yourpassword #Mot de passe de la base de données
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table #La valeur par défaut reste inchangée
store.db.branchTable=branch_table #La valeur par défaut reste inchangée
store.db.queryLimit=100
store.db.lockTable=lock_table #La valeur par défaut reste inchangée
store.db.maxWait=5000
store.redis.host=127.0.0.1
store.redis.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.database=0
store.redis.password=null
store.redis.queryLimit=100
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

ps: Il y a une ligne vide inutile au bas du profil , Certains internautes disent qu'il y aura une exception si vous ne supprimez pas ,Proposition de suppression.

Sera pertinentseataDeconfig.txt Configurer l'importation dans nacosMoyenne
Cette partie de l'opération a fait beaucoup de recherches sur Internet , Il semble que les deux scripts suivants soient utilisés pour synchroniser les données , Ici, je vais poster le script pour que vous puissiez le voir. :

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

Notez qu'il fautconfig.txt Copie des documents à et bin L'emplacement du répertoire au même niveau est acceptable :
Photos: https://uploader.shimo.im/f/cIpElXNIdT4GjbE8.png

C'est ce que j'ai dit au début de l'article. config.txt Pourquoi ce fichier est - il arrivé? .
Exécuter le script

sh nacos-config.sh localhost

Voir après l'importation réussie nacosInterface

Insérer la description de l'image ici

Puis on recommence seata-server

Script de démarrage simple :

echo "======== prepare to start seata ======="
nohup sh ./seata-server.sh &
echo "======== now, it is starting ======="

Un démarrage réussi peut voir la sortie d'une telle session dans le journal :

2020-11-07 15:31:53.116 INFO [main]io.seata.core.rpc.netty.RpcServerBootstrap.start:155 -Server started ... 
2020-11-07 15:34:43.032 INFO [UndoLogDelete_1]io.seata.server.coordinator.DefaultCoordinator.undoLogDelete:359 -no active rm channels to delete undo log

EtnacosIl y a un lienseataInscription au service
Insérer la description de l'image ici

Et puis il y a java Le Code du programme est entré. .
Cas de code complet du projet :

mavenConfiguration dépendante


<?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">
    <parent>
        <artifactId>dubbo-service-tcc</artifactId>
        <groupId>org.idea</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>dubbo-service-pay</artifactId>

    <properties>
        <mysql.version>5.1.26</mysql.version>
        <druid.version>1.1.10</druid.version>
        <seata.version>1.1.0</seata.version>
        <nacos.client.version>1.1.3</nacos.client.version>
        <nacos.springboot.starter.version>0.2.1</nacos.springboot.starter.version>
        <dubbo.service.interface.version>1.0-SNAPSHOT</dubbo.service.interface.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>${
    seata.version}</version>
        </dependency>

        <dependency>
            <groupId>org.idea</groupId>
            <artifactId>dubbo-service-interface</artifactId>
            <version>${
    dubbo.service.interface.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${
    mysql.version}</version>
        </dependency>
        <!-- Dubbo Registry Nacos -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
            <version>${
    dubbo.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>${
    nacos.client.version}</version>
        </dependency>

        <!-- 1. nacos- Si vous souhaitez injecter des fonctionnalités, vous devez utiliser starter -->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-config-spring-boot-starter</artifactId>
            <version>${
    nacos.springboot.starter.version}</version>
        </dependency>
        <!-- 2. nacos- Service Discovery Functional Dependence  -->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-discovery-spring-boot-starter</artifactId>
            <version>${
    nacos.springboot.starter.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${
    druid.version}</version>
        </dependency>

    </dependencies>

</project>

Profil

Configurer la source de donnéesapplication.propertieDocumentation

server.port=8081
spring.application.name=seata-demo
# goods
spring.datasource.goods.jdbc-url=jdbc:mysql://prod.min.mall.com:3306/mall-goods?useUnicode=true&characterEncoding=utf8
spring.datasource.goods.username=root
spring.datasource.goods.password=root
spring.datasource.goods.driver-class-name=com.mysql.jdbc.Driver
# order
spring.datasource.order.jdbc-url=jdbc:mysql://prod.min.mall.com:3306/mall-order?useUnicode=true&characterEncoding=utf8
spring.datasource.order.username=root
spring.datasource.order.password=root
spring.datasource.order.driver-class-name=com.mysql.jdbc.Driver

À propos deseata La configuration est principalement écrite dans application.ymlMoyenne:

seata:
  enabled: true
  application-id: tcc-seata-service
  tx-service-group: SEATA_GROUP # Groupe de transaction( Peut être nommé indépendamment pour chaque application , Vous pouvez également utiliser le même nom )
  client:
    rm-report-success-enable: true
    rm-table-meta-check-enable: false #  Rafraîchir automatiquement la structure de la table dans le cache (Par défautfalse)
    rm-report-retry-count: 5 #  Rapport des résultats de la phase I TCNombre de retraits(Par défaut5)
    rm-async-commit-buffer-limit: 10000 #  Longueur de la file d'attente du cache de commit asynchrone (Par défaut10000)
    rm:
      lock:
        lock-retry-internal: 10 # Vérifier ou utiliser l'intervalle global de retry de verrouillage(Par défaut10ms)
        lock-retry-times:    30 # Vérifier ou utiliser le nombre de retraits de verrouillage global(Par défaut30)
        lock-retry-policy-branch-rollback-on-conflict: true #  Politique de verrouillage en cas de conflit entre une transaction de branche et une autre transaction globale de rollback ( Priorité accordée à la libération de la serrure locale pour un retour en arrière réussi )
    tm-commit-retry-count:   3 # Phase I Global Submission results EscalationTCNombre de retraits(Par défaut1Une fois,Recommandé plus grand que1)
    tm-rollback-retry-count: 3 # Phase I global ROLLBACK Results ReportTCNombre de retraits(Par défaut1Une fois,Recommandé plus grand que1)
    undo:
      undo-data-validation: true # Vérification du miroir de recul en deux étapes(Par défauttrueOuvert)
      undo-log-serialization: jackson # undoMéthode de sérialisation(Par défautjackson)
      undo-log-table: undo_log  # PersonnalisationundoNom du tableau(Par défautundo_log)
    log:
      exceptionRate: 100 #  Probabilité de sortie anormale du Journal (Par défaut100)
    support:
      spring:
        datasource-autoproxy: true
  service:
    vgroup-mapping:
              my_tcc: default # TC Cluster(Doit être compatible avecseata-serverSoyez cohérent)
    enable-degrade: false # Interrupteur de rétrogradation
    disable-global-transaction: false # Désactiver les transactions globales(Par défautfalse)
    grouplist:
       default: 127.0.0.1:8091
  transport:
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      server-executor-thread-prefix: NettyServerBizHandler
      share-boss-worker: false
      client-selector-thread-prefix: NettyClientSelector
      client-selector-thread-size: 1
      client-worker-thread-prefix: NettyClientWorkerThread
    type: TCP
    server: NIO
    heartbeat: true
    serialization: seata
    compressor: none
    enable-client-batch-send-request: true #  Si la demande de message de transaction du client est envoyée par fusion en vrac (Par défauttrue)
  registry:
    file:
      name: file.conf
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace:
      cluster: default
  config:
    file:
      name: file.conf
    type: nacos
    nacos:
      namespace:
      server-addr: localhost:8848
 Il y a des trous dedans. , Il est recommandé que le module de configuration inférieur seataDeserver La configuration finale est la même ,Sinon, il y aura une erreur.
 service:
    vgroup-mapping:
              my_tcc: default # TC Cluster(Doit être compatible avecseata-serverSoyez cohérent)
       grouplist:
       default: 127.0.0.1:8091 

seata:       
     tx-service-group: SEATA_GROUP # J'expliquerai cette propriété plus tard. 

dubbo.propertiesProfil

dubbo.application.id=dubbo-service
dubbo.application.name=dubbo-service
dubbo.registry.address=nacos://localhost:8848
dubbo.provider.threads=10
dubbo.provider.threadpool=fixed
dubbo.provider.loadbalance=roundrobin
dubbo.server=true
dubbo.protocol.name=dubbo
dubbo.protocol.port=9091
dubbo.protocol.threadpool=fixed
#dubbo.protocol.dispatcher=execution
dubbo.protocol.threads=100
dubbo.protocol.accepts=100
dubbo.protocol.queues=100

Structure globale du projet:

Photos: https://uploader.shimo.im/f/QrxvdqtVRWP3Ttsn.png

Module de code

Configuration de la source de données associée

package org.idea.service.pay.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/** * @author idea * @date created in 6:12 Après - midi 2020/11/14 */
@Configuration
public class DataSourceConfig {
    

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.order")
    public DataSource orderDataSource(){
    
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.goods")
    public DataSource goodsDataSource(){
    
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "orderJdbcTemplate")
    public JdbcTemplate orderJdbcTemplate(@Qualifier("orderDataSource")DataSource orderDataSource){
    
        return new JdbcTemplate(orderDataSource);
    }

    @Bean(name = "goodsJdbcTemplate")
    public JdbcTemplate goodsJdbcTemplate(@Qualifier("goodsDataSource")DataSource goodsDataSource){
    
        return new JdbcTemplate(goodsDataSource);
    }
}

daoCouche

package org.idea.service.pay.dao;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

/** * @author idea * @date created in 6:17 Après - midi 2020/11/14 */
@Repository
public class GoodsDao {
    

    @Resource
    private JdbcTemplate goodsJdbcTemplate;

    public boolean updateStock(int id,int stock){
    
        String sql = "update t_goods set stock=stock-? where id=?";
        int result = goodsJdbcTemplate.update(sql,new Object[]{
    stock,id});
        return result>0;
    }


}
package org.idea.service.pay.dao;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.UUID;

/** * @author idea * @date created in 6:17 Après - midi 2020/11/14 */
@Repository
public class OrderDao {
    

    @Resource
    private JdbcTemplate orderJdbcTemplate;

    public boolean insertOrder(){
    
        int i=0/0; 
        String orderNo = UUID.randomUUID().toString();
        String sql = "INSERT INTO `t_order`( `order_no`, `user_id`, `goods_id`, `stock`, `unit`, `status`, `valid_status`, `create_time`, `update_time`) VALUES ( '" +
                orderNo+"', 1, 1, 1, 'Pièce (s)', 1, 1, NOW(), NOW())";
        orderJdbcTemplate.execute(sql);
        return true;
    }
}

IninsertOrder J'ai écrit une exception. , Pour les tests ultérieurs seataUtiliser.

serviceCouche

package org.idea.service.pay.service;

import io.seata.spring.annotation.GlobalTransactional;
import org.apache.dubbo.config.annotation.Service;
import org.idea.interfaces.pay.ITccService;
import org.idea.service.pay.dao.GoodsDao;
import org.idea.service.pay.dao.OrderDao;

import javax.annotation.Resource;

/** * @author idea * @date created in 7:41 Après - midi 2020/11/14 */
@Service(interfaceName = "iTccService")
public class ITccServiceImpl implements ITccService {
    

    @Resource
    private GoodsDao goodsDao;
    @Resource
    private OrderDao orderDao;


    @GlobalTransactional(timeoutMills = 300000,name = "tcc-seata-service-group")
    @Override
    public void doTcc(){
    
        System.out.println("====== Début des opérations ====== ");
        goodsDao.updateStock(1,1);
        orderDao.insertOrder();
        System.out.println("======  Fin de la transaction d'exécution  ====== ");
    }
}

Notez ce qui est écrit dans le Code ici @GlobalTransactionalNote ouiseata Points clés pour saisir les transactions distribuées ,
Vous pouvez voir ce que j'ai écrit ici. name- Oui.:tcc-seata-service-group, Ceci peut être personnalisé sans affecter .

Classe de démarrage

package org.idea.service.pay;

import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.idea.interfaces.pay.ITccService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/** * @author idea * @date created in 6:00 Après - midi 2020/11/14 */
@SpringBootApplication
@EnableDubbo
@RestController
public class Application {
    

    @Reference
    private ITccService iTccService;

    @GetMapping(value = "tcc")
    public String doTcc(){
    
        iTccService.doTcc();
        return "success";
    }

    public static void main(String[] args) {
    
        SpringApplication.run(Application.class);
        System.out.println("Application tcc demo");
    }
}

Programme de démarrage, Nous pouvons voir qu'il y a une corrélation dans le journal seata Impression du Journal d'information :
Insérer la description de l'image ici

seata Validation des transactions distribuées pour

Vérification des exceptions

Interface demandéehttp://localhost:8081/tcc
Dans la phase de création de l'ordre ,Une exception s'est produite,seata Le processus de retour en arrière a été effectué , L'inventaire de la base de données reste le même qu'au début . Il n'y a pas non plus de données supplémentaires pour la Bibliothèque de commandes .
Photos: https://uploader.shimo.im/f/23U8LEdMBNEDmJAc.png

Validation normale du processus
Supprimer le Code d'exception ,Nouvelle demande:
Photos: https://uploader.shimo.im/f/ni70tKjfCjz9Av7T.png

L'impression du journal est maintenant normale ,seata L'environnement de base a été construit avec succès !

Anomalies possibles

io.seata.common.exception.FrameworkException: No available service

Je me demande si les lecteurs rencontreront une exception similaire dans l'environnement final de validation de l'interface comme moi. , Cette exception est la suivante :
Photos: https://uploader.shimo.im/f/JCLpd3qxgM0IMQr7.png

Des recherches ont été effectuées sur Internet pour des informations pertinentes , J'ai rarement vu la raison de l'expliquer. , J'ai dû me gratter la tête. debugAnalyse.
Photos: https://uploader.shimo.im/f/GG9cgBN0ijvI9EQn.png

Combinez - vous avec ce que vous faisiez auparavant pour nacos Expérience de la compréhension du code source , Localiser rapidement le problème :

io.seata.config.nacos.NacosConfiguration#getConfig

Photos: https://uploader.shimo.im/f/8DuZaAD1MVQWFq3F.png

Demande ici dataIdPourservice.vgroupMapping.SEATA_GROUP,groupLa valeur est:SEATA_GROUP,Et voilà.nacos Première requête , Trouvé cette configuration n'existe pas ,Alors ajoutez manuellement:

En fait, c'est ici que dataIdCaudalSEATA_GREOUP Le nom est mentionné dans notre article. application.yml Un paramètre configuré dans .

Je connais la correspondance.dataid,group, Conjecture pertinente value Ça devrait aller avec ymlD'accord., Alors j'ai configuré default.
Photos: https://uploader.shimo.im/f/bVX8SLC6iiSKGLte.png

Résultat la console a changé de réponse immédiatement :
Photos: https://uploader.shimo.im/f/FK9U7aYUbFWHdN3T.png

Retry l'interface maintenant , C'est revenu à la normale .

Résumé

La mise en file d'attente des messages a été utilisée pour assurer la cohérence finale des transactions dans un environnement distribué. ,Pourseata Les principes de ce cadre technique sont encore loin d'être connus. .
Rangez et peignez ,Sensationsseata Plus que nacos,dubbo Ces intergiciels sont plus hauts ,Configuration complexe,Détails fastidieux. Après avoir lu l'introduction sur le site officiel , Ce n'est que atModèle, Il reste encore beaucoup à apprendre sur d'autres modèles de transaction plus complexes. .

版权声明
本文为[Danny... Idea]所创,转载请带上原文链接,感谢
https://chowdera.com/2022/01/202201080559031769.html

随机推荐