5. Spring Cloud: Transacciones distribuidas de Seata

Tabla de contenido

1. Introducción de Seata

Conceptos básicos de Seata

Más principios y análisis de procesos

2. El caso de no control de transacciones distribuidas

3. Concepto de asiento

Interpretación del concepto de agrupación empresarial

 5. Instalación y configuración del servidor Seata

 6. Cliente Seata (modo AT)

 Siete, configuración de ajuste del servidor Seata

Apéndice A: scripts oficiales como SQL requeridos por Seata


1. Introducción de Seata

El nombre completo de seata es: s implementar  una  arquitectura de transacción autónoma  extensible  , la traducción literal al chino es : arquitectura de transacción autónoma, extensible y simple  .

Como solución y gestión de transacciones distribuidas entre bases de datos, Seata proporciona cuatro soluciones de modo de transacción: AT, TCC, Saga y XA. Entre ellos, el modo AT tiene casi cero modificaciones y cero intrusiones en el código comercial. Para una introducción relacionada, consulte el sitio web oficial.

  Seata se divide en un servidor (Seata Server) y un cliente (es decir, cada microservicio). El servidor es el centro de coordinación global de TC, y el cliente es el TM y RM.

 Conceptos básicos de Seata

En la arquitectura de Seata hay tres roles:

  • TC  (Coordinador de transacciones) - Coordinador de transacciones: mantiene el estado de las transacciones globales y de sucursales, e impulsa la confirmación o reversión de transacciones globales . (Personalmente, siento que GTC es más intuitivo si se llama "Coordinador de transacciones global")
  • TM  (Administrador de transacciones) - Administrador de transacciones: defina el alcance de las transacciones globales , inicie transacciones globales, confirme o revierta transacciones globales.
  • RM  (Administrador de recursos) - Administrador de recursos: administra los recursos (Recurso) de las transacciones de la sucursal , habla con TC para registrar las transacciones de la sucursal e informar el estado de las transacciones de la sucursal, e impulsa el compromiso o la reversión de las transacciones de la sucursal .

 Entre ellos, TC es un servidor Seata-Server implementado por separado, TM y RM son clientes  integrados en cada aplicación de microservicio   .

En Seata, el diagrama del ciclo de vida de una transacción distribuida es el siguiente (la flecha verde es el enlace de llamada entre servicios, y la transacción global xid se transfiere automáticamente durante el proceso de transferencia):

Nota: El iniciador de la transacción global determina la confirmación o reversión general de la transacción global. Después de que el iniciador toma una decisión, informa al TC, y el TC notifica a cada servicio que realice la operación de compromiso/reversión local de la transacción de rama en cada servicio.

 Más principios y análisis de procesos

Consulte el artículo: [Gráfico] Análisis de código fuente de transacciones distribuidas en modo AT de Seata_Tiempo antiguo|Blog de YoungChen-Blog de CSDN

2. El caso de no control de transacciones distribuidas

   En el proyecto anterior de Java ( 4. SpringCloud: Control de flujo de servicios de Sentinel y blog de columna-CSDN de degradación de servicios_zyplanke ) ya hay servicios de pago y servicios de pedidos. Sobre la base de su ingeniería, modificarlo (principalmente aumentar el acceso a la base de datos)

 1. Agregue tres dependencias de spring-boot-starter-data-jpa, mysql-connector-java y druid-spring-boot-starter a pom.xml . El contenido es el siguiente:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.11</version>
        <relativePath/>
    </parent>

    <modules>
        <module>payment</module>
        <module>order</module>
    </modules>

    <groupId>com.example</groupId>
    <artifactId>myproject-global-pom</artifactId>
    <version>0.0.1-DEFAULT</version>
    <packaging>pom</packaging>
    <description>This is my project global pom config</description>
    <properties>
        <java.version>1.8</java.version>
        <springcloud.version>3.0.4</springcloud.version>
        <springcloudalibaba.version>2021.1</springcloudalibaba.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${springcloudalibaba.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>${springcloudalibaba.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
            <version>${springcloud.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>${springcloud.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            <version>${springcloud.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>${springcloudalibaba.version}</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <version>1.8.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

 2. Servicio de pago: defina la tabla de la base de datos del servicio de pago del motor InnoDB (e inserte un dato)

mysql> create schema payment;
mysql>  create table payment.T_PAY(order_id int, amount int, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY `PK_T_PAY` (order_id) ) ENGINE = InnoDB;
Query OK, 0 rows affected (0.02 sec)

mysql> desc payment.T_PAY;
+-------------+----------+------+-----+-------------------+-------------------+
| Field       | Type     | Null | Key | Default           | Extra             |
+-------------+----------+------+-----+-------------------+-------------------+
| order_id    | int      | YES  |     | NULL              |                   |
| amount      | int      | YES  |     | NULL              |                   |
| create_time | datetime | YES  |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
+-------------+----------+------+-----+-------------------+-------------------+
3 rows in set (0.00 sec)

  3. Servicio de pago: modifique el PaymentController.java del servicio de pago, el contenido es el siguiente (agregue la base de datos SQL, cambie el parámetro de URL a ID de usuario)

@RestController
public class PaymentController {
    @Value("${server.port}")
    private int myport;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    Logger logger = LoggerFactory.getLogger(PaymentController.class);

    @GetMapping("/dopay/{orderid}")
    public ResponseEntity<String> paylogic(@PathVariable("orderid") Long orderid) {
        int insert_rows = jdbcTemplate.update("insert into T_PAY (order_id, amount) values (" + orderid +", 999)");
        logger.info("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
        return ResponseEntity.ok("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
    }
}

 4. Servicio de pago: dado que este servicio utiliza Nacos como centro de configuración, modifique la configuración de Data ID=paymentService.properties en Nacos y agregue la configuración de la información de conexión de la base de datos de la siguiente manera:

5. Servicio de pedidos: defina la tabla de la base de datos del servicio de pedidos (motor InnoDB)

mysql> create schema orders;
mysql> create table orders.T_Order (order_id int, address varchar(128), remark varchar(256), create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY `PK_T_Order` (order_id) ) ENGINE = InnoDB ;
Query OK, 0 rows affected (0.02 sec)

mysql> desc orders.T_Order;
+-------------+--------------+------+-----+-------------------+-------------------+
| Field       | Type         | Null | Key | Default           | Extra             |
+-------------+--------------+------+-----+-------------------+-------------------+
| order_id    | int          | YES  |     | NULL              |                   |
| address     | varchar(128) | YES  |     | NULL              |                   |
| remark      | varchar(256) | YES  |     | NULL              |                   |
| create_time | datetime     | YES  |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
+-------------+--------------+------+-----+-------------------+-------------------+
4 rows in set (0.00 sec)

  6. Servicio de pedidos: Modificar el OrderController.java del servicio de pedidos. Llame primero al servicio de pago y luego simule la excepción. El contenido es el siguiente 

@RestController
public class OrderController {

    @Autowired
    private IPaymentServiceClient paymentServiceClient;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    Logger logger = LoggerFactory.getLogger(OrderController.class);
    boolean forcefail = true;

    @GetMapping("/consumer/{orderid}")
    public ResponseEntity<String> consumerFeign(@PathVariable("orderid") Long orderid) {
        String result = paymentServiceClient.dodopay(orderid).getBody(); // 调用支付服务进行支付
        logger.info("[paymentService result]: {}", result);

        jdbcTemplate.update("insert T_Order(order_id, address, remark) values (" + orderid + ", 'dizhi', '"+result+"')");

        //模拟在支付完成后,本订单事务后续处理过程中发生异常后,全局回滚功能
        if (forcefail) {
            throw new RuntimeException("模拟在支付完成后,本订单事务后续处理过程中发生异常!");
        }

        return ResponseEntity.ok ("调用订单服务完成。 订单orderid:" + orderid + ",   调用支付返回的Body:" + result);
    }
}

  6. Servicio de pedidos: dado que este servicio utiliza Nacos como centro de configuración, modifique la configuración de Data ID=orderService.properties en Nacos y agregue la configuración de la información de conexión de la base de datos de la siguiente manera

 7. Prueba. enrollamiento http://localhost:8888/consumer/66

 8. Conclusión: En el proceso de pagar primero y luego ordenar en el almacén, una vez que se completa el pago, se produce una excepción en el procesamiento posterior de la transacción del pedido, que debería haber sido devuelto en su totalidad. Sin embargo, de hecho, los registros en la tabla de pagos aún existen y no se han revertido en su totalidad, lo que no logra lograr la consistencia de las transacciones entre bases de datos.

Por lo tanto, es necesario utilizar componentes de transacciones distribuidas como Seata para resolver este tipo de problemas.

3. Concepto de asiento

Lea primero los documentos del sitio web oficial (los conocimientos requeridos incluyen):

Interpretación del concepto de agrupación empresarial

Entre ellos, la mayoría de los artículos en Internet son engañosos sobre la agrupación de transacciones y varios parámetros conceptuales relacionados. El entendimiento correcto es:

agrupación de transacciones tx-service-group

La agrupación de transacciones es el concepto de lógica de recursos de seata.

Es decir, las transacciones se pueden agrupar lógicamente según las necesidades de los microservicios, y cada grupo toma un nombre

Configurado del lado del cliente (microservicio).

La forma de los parámetros de configuración en Spring es la siguiente:

seata.tx-service-group= mytest-tx-group

O use la configuración spring.cloud.alibaba.seata.tx-service-group

Si no se especifica anteriormente, la regla predeterminada para el nombre del grupo de transacciones lógicas es: spring.application.name value + "-seata-service-group"

mapeo de grupos virtuales

Es decir, el nombre del clúster clúster compuesto por nodos de servidor seata-server.

En el cliente (microservicio), es necesario especificar el vgroup-mapping utilizado para el mapeo (es decir, el clúster del lado del servidor) para cada grupo de transacciones lógicas.

Al ajustar esta relación de asignación, puede cambiar a un clúster de servidor diferente.

Configurado del lado del cliente (microservicio).

Especifique la agrupación de transacciones lógicas que se asignará a vgroup-mapping (el valor de vgroup-mapping debe ser coherente con el clúster en Registry.conf en seata-server).

La forma de los parámetros de configuración en Spring es la siguiente:

seata.service.vgroup-mapping. mytest-tx-group = beijing

Entre ellos: rojo es el nombre del grupo de transacciones

El valor del lado derecho del signo igual no necesita comillas; es el mismo que el nombre del clúster de seata-server

lista de grupos

No es el mismo concepto de nivel que el anterior.

Su propósito es configurar directamente la IP y la información del puerto de cada nodo del servidor del servidor de asiento para el mapeo de grupos virtuales (es decir, el clúster del servidor) en el cliente.

Configurado del lado del cliente (microservicio).

Se recomienda usar seata.registry.type=nacos u otros tipos (el cliente de microservicio y el servidor de seata-server deben especificarse como nacos al mismo tiempo), porque usando Nacos, el cliente puede obtener automáticamente la IP de seata -Servidor de nodo de servidor a través de Nacos e información del puerto.

Si no es un centro de registro como nacos, debe especificar directamente la información del nodo del servidor seata-server a través de los siguientes dos parámetros en la configuración del cliente (microservicio):

Los parámetros de configuración en Spring son los siguientes (oficialmente no recomendados):

seata.registry.type=archivo

seata.service.grouplist. beijing = seata-serverIP:Puerto

Entre ellos: púrpura es el nombre de mapeo de vgroup (es decir, el nombre del clúster de seata-server)

Los errores comunes en los artículos en línea son:

  1. Estos conceptos en Internet no están claros (de hecho, se debe principalmente a documentos del sitio web oficial de Seata mal escritos, que son causados ​​​​por el plagio de muchos artículos en Internet sin pensar);
  2. El entendimiento erróneo en Internet es que los tres parámetros anteriores aún deben configurarse en el servidor del servidor seata. De hecho, estos parámetros se configuran del lado del cliente, no del lado del servidor;
  3. El error en línea requiere que los archivos registration.conf y file.conf se copien en el directorio de recursos del cliente (microservicio), lo cual no es necesario para las aplicaciones SpringBoot.
  4. Otros: Los documentos y ejemplos en el sitio web oficial son inconsistentes con la última versión de seata y existen contradicciones entre los documentos. (Por ejemplo, " -seata-service-group " arriba , el documento del sitio web oficial todavía está escrito incorrectamente como " -fescar-service-group " en el capítulo <Agrupación de transacciones> a mediados de diciembre de 2021).


4. Introducción ambiental

  • Seata server seata-server: Implementación de Seata Server y Seata necesita administrar la base de datos MySQL específica de transacciones
  • Cliente Seata: cada microservicio. Es decir en este caso incluyen: servicio de pago y servicio de pedido.

 5. Instalación y configuración del servidor Seata

1. Descarga y descomprime seata-server

wget https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.zip
unzip seata-server-1.4.2.zip

Después de la descompresión, el resultado del directorio es:

├──bin 
├──conf 
├──log 
└──lib

2. Configure el servidor seata. El archivo de configuración se puede ver en el directorio conf del servidor de Seata:

  • registro.conf : este archivo debe existir. Defina lo que se necesita para seata-server: centro de registro de registro y centro de configuración de configuración. en:
    • registro.tipo tipo de registro rango opcional: archivo, nacos, eureka, redis, zk, consul, etcd3, sofa. Por lo general, elija el centro de configuración de nacos como centro de registro.
    • config.type El tipo de centro de configuración se puede seleccionar entre: file, nacos, apollo, zk, consul, etcd3.
  • file.conf : este archivo file.conf solo es necesario cuando elige configurar Registry.type=file o config.type=file en el archivo Registry.conf del lado del servidor (el almacén del modo de almacenamiento de la administración de transacciones distribuidas se puede especificar en este archivo .mode)
  • Ya sea archivo, nacos u otras fuentes de configuración. Ambos deben definir el modo de almacenamiento store.mode de la gestión de transacciones distribuidas. Hay tres modos de almacenamiento: archivo, db, redis.
    • Si se selecciona store.mode=file, la información relacionada con la transacción de Seata se almacenará en la memoria y persistirá en el archivo root.data local. El rendimiento es relativamente alto. 
    • Si elige store.mode=db. La información relacionada con las transacciones de Seata se almacena en una base de datos. El uso del modo de base de datos puede admitir múltiples inicios de servidor seata para lograr una alta disponibilidad de servidor seata.

Para simplificar, este artículo:

  • En el archivo registration.conf, establezca Registry.type=nacos (y apunte serverAddr en nacos al servidor Nacos, y cambie el clúster a beijing); config.type=file.
  • Configure store.mode=file en el archivo file.conf.

3. Inicie el servidor seata-server

./seata-server.sh -h 39.100.80.168 &

还可以加启动选项:
    --help 显示帮助
    -h: 本Seata服务向Nacos等注册时,以哪个IP信息作为Seata服务对外暴露IP(本选项不是指定本地侦听IP)。
        对于服务器是阿里云ESC等有NAT转换特别有用
    -p: The port to listen. Default: 8091
    -m: 服务端undo log store mode : file、db. Default: file
    -n: Server node,多个Server时,需区分各自节点,用于生成不同区间的transactionId,以免冲突
    -e: 多环境配置参考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html

4. Verifique el resultado del registro. Porque Registry.type=nacos está configurado en el archivo Registry.conf. Para que puedas ver el servicio registrado en Nacos.

Haga clic para ver los detalles y preste atención para ver la información del clúster (el nombre del clúster en la siguiente figura proviene de la configuración del clúster en Registry.conf):

 6. Cliente Seata (modo AT)

   1. Se debe usar Seata y se deben agregar dependencias al proyecto de ejemplo anterior Maven. pom.xml es el siguiente:

<!-- 前面省略 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <version>${springcloudalibaba.version}</version>
        </dependency>
<!-- 后面省略 -->
  • Lo anterior pasó spring-cloud-starter-alibaba-seata:2021.1 versión, pasó seata-spring-boot-starter:1.3.0, y la dependencia real es seata:1.3.0 versión.
  • Si se usa la versión de MySQL ≥ 8.0.23, se informará un error " isrduparser.JacksonUndoLogParser: excepción de decodificación json, no se puede construir una instancia de `java.time.LocalDateTime` (no existen creadores, como el constructor predeterminado): no se puede deserializar del valor del objeto (sin creador basado en delegados o propiedades) ". Seata dijo oficialmente que este problema se solucionó en 1.4.2, pero la prueba real encontró que Seata1.4.2 aún no se ha solucionado. Por lo tanto, la versión de la base de datos MySQL puede evitar este error porque es menor o igual a 8.0.22. Consulte https://github.com/seata/seata/issues/3620# para obtener más información.

Otro:

  • La dependencia anterior usa spring-cloud-starter-alibaba-seata en lugar de seata-spring-boot-starter. Debido a que spring-cloud-starter-alibaba-seata ha interceptado SeataHandlerInterceptor entre servicios para transferir información XID de transacción global cuando se llama a cada servicio, se puede usar directamente, lo que es más conveniente.
  • Si la dependencia no es usar seata-spring-boot-starter (o spring-cloud-starter-alibaba-seata), sino usar seata-all directamente, debe escribir un código de proxy de fuente de datos adicional. Porque seata-spring-boot-starter ha implementado un proxy de origen de datos y seata.enable-auto-data-source-proxy es verdadero de forma predeterminada. Para obtener más información, consulte las "Preguntas frecuentes de Seata n.º 20" http://seata.io/zh-cn/docs/overview/faq.html

   2. En la base de datos local de cada microservicio, asegúrese de que las tablas de negocios que participan en la transacción distribuida tengan claves primarias PK (de lo contrario, Seata informará un error " No se pudo encontrar ningún índice en la tabla "). Seata actualmente solo admite claves primarias de una sola columna y no admite claves primarias compuestas. Para obtener más información, consulte las "Preguntas frecuentes n.º 13" oficiales de Seata http://seata.io/zh-cn/docs/overview/faq.html

   3. Debido al uso del modo Seata AT, es necesario crear una tabla undo_log en la base de datos de la aplicación utilizada por cada microservicio (esta no es la base de datos del servidor de Seata, sino la base de datos comercial de cada microservicio en el lado del cliente). ).

     Para conocer la fuente del script de sentencia SQL para crear tablas, consulte el Apéndice A de este documento. Aquí se utiliza el modo AT, tomando como ejemplo MySQL ( https://github.com/seata/seata/blob/develop/script/client/at/db/mysql.sql ) Las bases de datos utilizadas por los dos servicios de pago y order son respectivamente Cree la siguiente tabla:

-- 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       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) 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';

   Nota: El nombre de la tabla `undo_log` se puede modificar. Si lo modifica, debe modificar la instrucción SQL de creación de tablas y el valor de configuración del microservicio seata.client.undo.log-table= al mismo tiempo

4. Configurar la información relacionada con Seata en cada microservicio. Dado que los propios servicios de pago y pedido son administrados por el centro de configuración de Nacos, y el contenido de configuración de los dos servicios es el mismo, la siguiente información de configuración relacionada con Seata se agrega a common commonshare.properties en el centro de configuración

#================Seata Client Config============
#是否启用Seata(默认启用)
seata.enabled=true

# 定义本服务的事务组(可以每个应用独立取名,也可以不同的应用使用相同的名字)
seata.tx-service-group=mytest-tx-group

# 指定逻辑事务组映射到vgroup-mapping(等号左侧为事务分组名,vgroup-mapping的值需要与seata-server中registry.conf中的cluster保持一致)
seata.service.vgroup-mapping.mytest-tx-group=beijing

seata.registry.type=nacos
seata.registry.nacos.server-addr=39.100.80.168:8848
seata.registry.nacos.application=seata-server
seata.registry.nacos.group=SEATA_GROUP

5. Agregue  la anotación @GlobalTransactional  a la entrada de todo el proceso comercial que requiere transacciones distribuidas. Aquí el servicio de pedidos de pedidos es el punto de entrada de toda la lógica de transacciones distribuidas, por lo que la anotación se agrega a OrderController.java, el contenido es el siguiente:

@RestController
public class OrderController {

    @Autowired
    private IPaymentServiceClient paymentServiceClient;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    Logger logger = LoggerFactory.getLogger(OrderController.class);

    boolean forcefail = true;

    @GetMapping("/consumer/{orderid}")
    @GlobalTransactional(timeoutMills = 28000, name = "gts-seata-example")
    public ResponseEntity<String> consumerFeign(@PathVariable("orderid") Long orderid) {
        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        String result = paymentServiceClient.dodopay(orderid).getBody(); // 调用支付服务进行支付
        logger.info("[paymentService result]: {}", result);

        jdbcTemplate.update("insert T_Order(order_id, address, remark) values (" + orderid + ", 'dizhi', '"+result+"')");

        //模拟在支付完成后,本订单事务后续处理过程中发生异常后,全局回滚功能
        if (forcefail) {
            throw new RuntimeException("模拟在支付完成后,本订单事务后续处理过程中发生异常!");
        }

        return ResponseEntity.ok ("调用订单服务完成。 订单orderid:" + orderid + ",   调用支付返回的Body:" + result);
    }
}

       Opciones de anotación: timeoutMills es el período de tiempo de espera, name es el nombre de la transacción y  rollbackFor especifica qué clases heterogéneas lanzadas por la función se revertirán globalmente (si no se especifica, el valor predeterminado es RuntimeException). noRollbackFor especifica qué clases heterogéneas lanzadas por la función no se revertirán globalmente.

Nota: La confirmación o reversión general de la transacción global está determinada por       el iniciador de la transacción global (es decir,  el servicio donde se encuentra @GlobalTransactional ). Es decir, si el commt se aplica globalmente, el iniciador notificará al TC (seata-server), y luego el TC notificará a cada sucursal para completar el envío local. Si se requiere la reversión global, el iniciador notificará al TC (servidor seata), y el TC notificará a cada sucursal para completar la reversión local. 

         Por lo tanto, en el enlace de llamada de servicio, la excepción debe lanzarse al iniciador de la transacción global, para que pueda ser percibida por la anotación @GlobalTransactional del iniciador de la transacción global.

6. (Opcional) Para observar mejor la transmisión de XID. Agregar inicio de sesión en PaymentController.java

@RestController
public class PaymentController {
    @Value("${server.port}")
    private int myport;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    Logger logger = LoggerFactory.getLogger(PaymentController.class);

    @GetMapping("/dopay/{orderid}")
    public ResponseEntity<String> paylogic(@PathVariable("orderid") Long orderid) {
        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        int insert_rows = jdbcTemplate.update("insert into T_PAY (order_id, amount) values (" + orderid +", 999)");
        logger.info("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
        return ResponseEntity.ok("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
    }
}

    7. Ejecute los servicios de pago y pedido respectivamente. prueba. enrollamiento http://localhost:8888/consumer/66   

          Se puede encontrar que las bases de datos comerciales de los dos servicios tienen datos vigentes al mismo tiempo o no tienen datos al mismo tiempo, lo que logra la coherencia de las transacciones distribuidas.

 Siete, configuración de ajuste del servidor Seata

  Lo anterior usa config.type=file, store.mode=file en seata-server. Ninguno de estos es adecuado para un entorno de producción. Por lo tanto, se requieren ajustes: cambie config.type=file al centro de configuración de Nacos y store.mode=file para que se almacene en la base de datos db.

   1. Obtenga el script SQL para la creación de tablas en la base de datos del servidor de Seata. Para conocer la fuente del script, consulte las instrucciones en el Apéndice A de este artículo (es decir, https://github.com/seata/seata/tree/develop/script/config-center )

 2. Ejecute el script SQL de creación de tablas. Primero cree un esquema llamado "seata" en la base de datos MySQL. Luego, bajo el esquema, ejecute el script SQL anterior (cuatro tablas: ` global_table` , ` branch_table` , ` lock_table` , ` distributed_lock` )

 3. Ingrese al directorio conf bajo el directorio de instalación del software del asiento del servidor y edite el archivo registration.conf, de la siguiente manera:

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

  nacos {
    application = "seata-server"
    serverAddr = "39.100.80.168:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "beijing"
    username = ""
    password = ""
  }
}

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

  nacos {
    serverAddr = "39.100.80.168:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = ""
    password = ""
    dataId = "seataServer.properties"
  }
}
  • Mantenga el registro de servicio registration.type=nacos sin cambios (es decir, Seata continúa usando el registro de nacos y la configuración específica de serverAddr, grupo, clúster, etc. bajo nacos depende de la situación real).
  • Modifique config.type=nacos (el serverAddr específico, el grupo, el dataId y otras configuraciones en nacos se basan en la situación real)

  4. Obtenga la plantilla de contenido del elemento de configuración del centro de configuración. Para conocer la fuente del contenido de configuración, consulte las instrucciones en el Apéndice A de este artículo (es decir, https://github.com/seata/seata/tree/develop/script/config-center )

  5. Obtenga el contenido del archivo "config.txt" como referencia de plantilla (consulte: https://seata.io/zh-cn/docs/user/configurations.html para conocer el significado de cada elemento de configuración ).

       Debido a que el archivo "config.txt" tiene mucho contenido original, debe elegirlo (otros elementos de configuración no necesitan mostrar la configuración, solo mantenga el valor predeterminado). Además, "config.txt" incluye la configuración del servidor de Seata y la definición del cliente de Seata (es decir, cada microservicio). Aquí solo se selecciona la configuración relacionada con el servidor y las más utilizadas, el contenido de la configuración modificada es el siguiente:

#========= Seata Server Config:Server端连接数据库信息
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=Pwd_1234
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

   Copie manualmente la configuración de contenido anterior a Nacos en la interfaz del centro de configuración de Nacos, como se muestra en la figura a continuación. El dataId=seataServer.properties, group=SEATA_GROUP, etc. en el centro de configuración debe ser coherente con los definidos en el archivo registration.conf . (La mayoría de los artículos en línea envían el contenido anterior a Nacos a través del script de shell de nacos-config.sh. Sin embargo, se recomienda configurarlo manualmente, lo cual es más conveniente y el proceso es autocontrolable)

 6. Reinicie el servidor seata. ./seata-server.sh -h 39.100.80.168 &

 7. Se puede encontrar en la prueba que si la transacción global general se envía con éxito o se retrotrae, no quedarán datos en las tres tablas de seata-server (el proceso de acción real de SQL es primero insertar, luego actualizar y finalmente borrar). Solo las transacciones globales inconclusas conservarán los datos en las tres tablas.

Apéndice A: scripts oficiales como SQL requeridos por Seata

ver directorio de instalaciónseata/conf/README-zh.md. 该文件内如如下:

# 脚本说明

## [client](https://github.com/seata/seata/tree/develop/script/client) 

> 存放用于客户端的配置和SQL

- at: AT模式下的 `undo_log` 建表语句
- conf: 客户端的配置文件
- saga: SAGA 模式下所需表的建表语句
- spring: SpringBoot 应用支持的配置文件

## [server](https://github.com/seata/seata/tree/develop/script/server)

> 存放server侧所需SQL和部署脚本

- db: server 侧的保存模式为 `db` 时所需表的建表语句
- docker-compose: server 侧通过 docker-compose 部署的脚本
- helm: server 侧通过 Helm 部署的脚本
- kubernetes: server 侧通过 Kubernetes 部署的脚本

## [config-center](https://github.com/seata/seata/tree/develop/script/config-center)

> 用于存放各种配置中心配置项模板和脚本,执行时都会读取 `config.txt`配置文件,并写入配置中心

- nacos: 用于向 Nacos 中添加配置
- zk: 用于向 Zookeeper 中添加配置,脚本依赖 Zookeeper 的相关脚本,需要手动下载;ZooKeeper相关的配置可以写在 `zk-params.txt` 中,也可以在执行的时候输入
- apollo: 向 Apollo 中添加配置,Apollo 的地址端口等可以写在 `apollo-params.txt`,也可以在执行的时候输入
- etcd3: 用于向 Etcd3 中添加配置
- consul: 用于向 consul 中添加配置

Supongo que te gusta

Origin blog.csdn.net/zyplanke/article/details/121216853
Recomendado
Clasificación