Integrating Seata with Nacos, Spring Cloud Alibaba, and OpenFeign: Resolving Transaction Rollback Failures During Fallback

Integrating distributed transaction management using Seata in a Spring Cloud Alibaba microservices architecture—especially when combined with OpenFeign fallbacks—can be error-prone due to version mismatches, misconfigured registries, missing Nacos configurations, or context propagation issues. This guide provides a working setup for Seata 1.6.1 with Nacos 2.2.0 and Spring Boot 2.6.13, focusing on resolving the common problem where global transactions fail during Feign client fallbacks.

Environment Versions

  • Spring Boot: 2.6.13
  • Spring Cloud: 2021.0.5
  • Spring Cloud Alibaba: 2021.0.5.0
  • Nacos Server & Client: 2.2.0
  • Seata: 1.6.1
  • OpenFeign: 3.1.5
  • LoadBalancer: 3.1.5
  • MyBatis-Plus: 3.5.1

Nacos Server Setup

Configure nacos/conf/application.properties with MySQL support:

# Enable MySQL
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root

Initialize the databace using mysql-schema.sql from the Nacos distribution. Start in standalone mode:

startup.cmd -m standalone

Seata Server Configuration

Create the required Seata tables in your MySQL database using scripts from seata/script/server/db/.

Edit seata/conf/application.yml:

seata:
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: fbb5b0a8-7b5f-413e-80b9-4010646e6378
      group: SEATA_GROUP
      data-id: seataServer.properties
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      namespace: fbb5b0a8-7b5f-413e-80b9-4010646e6378
      group: SEATA_GROUP
  store:
    mode: db
    db:
      url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
      user: root
      password: root

Nacos Configuration for Seata

In the Nacos console:

  1. Create a namespace with ID: fbb5b0a8-7b5f-413e-80b9-4010646e6378
  2. Switch to this namespace and create a configuration with Data ID: seataServer.properties

Use the template from seata/script/config-center/config.txt. Ensure the following line is present:

service.vgroupMapping.default_tx_group=default

Also create a separate configuration with Data ID: service.vgroupMapping.default_tx_group and value: default.

Client-Side (Microservice) Configuration

In application.yml:

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        group: SEATA_GROUP
        namespace: fbb5b0a8-7b5f-413e-80b9-4010646e6378

feign:
  circuitbreaker:
    enabled: true

seata:
  tx-service-group: default_tx_group
  enable-auto-data-source-proxy: true
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      application: seata-server
      namespace: fbb5b0a8-7b5f-413e-80b9-4010646e6378
      cluster: default
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      data-id: seataServer.properties
      namespace: fbb5b0a8-7b5f-413e-80b9-4010646e6378

Required Database Table

Create the undo_log table in each participating service’s database:

CREATE TABLE undo_log (
  id BIGINT AUTO_INCREMENT,
  branch_id BIGINT NOT NULL,
  xid VARCHAR(100) NOT NULL,
  context VARCHAR(128) NOT NULL,
  rollback_info LONGBLOB NOT NULL,
  log_status INT NOT NULL,
  log_created DATETIME NOT NULL,
  log_modified DATETIME NOT NULL,
  ext VARCHAR(100),
  PRIMARY KEY (id),
  UNIQUE KEY ux_undo_log (xid, branch_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

Fixing Seata Context Loss During Feign Fallback

When Feign fallbacks are enabled via Hystrix, the Seata XID (transaction ID) is lost because fallbacks run in separate threads. To propagate the XID:

1. Propagate XID to Feign Requests

@Component
@ConditionalOnProperty(name = "feign.circuitbreaker.enabled", havingValue = "true")
public class SeataFeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        String xid = RootContext.getXID();
        if (xid != null) {
            template.header(RootContext.KEY_XID, xid);
        }
    }
}

2. Preserve Request Context in Hystrix Threads

@Component
@ConditionalOnProperty(name = "feign.circuitbreaker.enabled", havingValue = "true")
public class SeataHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        String xid = RootContext.getXID();
        return new Callable<T>() {
            public T call() throws Exception {
                try {
                    RequestContextHolder.setRequestAttributes(attrs);
                    if (xid != null) {
                        RootContext.bind(xid);
                    }
                    return callable.call();
                } finally {
                    RootContext.unbind();
                    RequestContextHolder.resetRequestAttributes();
                }
            }
        };
    }
}

Register this strategy early in your application lifecycle (e.g., via a @PostConstruct method or static initializer).

3. Manual Transaction Control (Optional)

If automatic propagation isn't sufficient, use an aspect to manage transactions manually when XID is missing:

@Aspect
@Component
@ConditionalOnProperty(name = "feign.circuitbreaker.enabled", havingValue = "true")
public class SeataFallbackTransactionAspect {
    @Before("execution(* com.example..service.*.*(..))")
    public void beginTransactionIfNeeded() throws TransactionException {
        if (RootContext.getXID() == null) {
            GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
            tx.begin(300000);
        }
    }

    @AfterThrowing(pointcut = "execution(* com.example..service.*.*(..))", throwing = "ex")
    public void rollbackOnException(Throwable ex) throws TransactionException {
        if (RootContext.getXID() != null) {
            GlobalTransactionContext.reload(RootContext.getXID()).rollback();
        }
    }

    @AfterReturning(pointcut = "execution(* com.example..service.*.*(..))", returning = "result")
    public void commitIfSuccess(Object result) throws TransactionException {
        if (result instanceof Boolean && (Boolean) result && RootContext.getXID() != null) {
            GlobalTransactionContext.reload(RootContext.getXID()).commit();
        }
    }
}

Important Notes

  • Avoid catching exceptions in business logic if you rely on automatic rollback.
  • Do not use dynamic data sources (e.g., dynamic-datasource-spring-boot-starter) without additional Seata proxy configuration.
  • Ensure all services (including API Gateway) share the same Nacos namespace and group to discover eachother.
  • If using Hystrix, configure timeouts via hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds, not Feign’s read timeout.

Dependency Management (Maven)

Include in parent POM:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
      <version>2021.0.5.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>io.seata</groupId>
      <artifactId>seata-spring-boot-starter</artifactId>
      <version>1.6.1</version>
    </dependency>
  </dependencies>
</dependencyManagement>

In service modules, include:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
  <exclusions>
    <exclusion>
      <groupId>io.seata</groupId>
      <artifactId>seata-spring-boot-starter</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Tags: Seata Nacos spring-cloud-alibaba openfeign distributed-transactions

Posted on Sun, 07 Jun 2026 17:01:01 +0000 by miligraf