How to Integrate Dubbo with Seata for Distributed Transactions in Spring Boot
This tutorial walks through integrating Dubbo with Seata in a Spring Boot microservice architecture, showing how to create service interfaces, configure Maven dependencies, set up Nacos discovery, implement distributed transaction logic, test normal and rollback scenarios, and externalize Seata server addresses for scalable deployments.
Background
In a previous article we introduced Seata’s architecture, deployment, and basic usage. Within the Spring Cloud ecosystem there are two ways to perform remote service calls: HTTP tools such as OpenFeign or HttpClient, and Dubbo which uses TCP for higher efficiency.
Because many domestic projects use Dubbo for remote calls, we will use Dubbo as an example to show how to integrate it with Seata for distributed transaction handling.
Solution Practice
We modify an existing project and illustrate the interaction flow with a diagram.
2.1 Create Service Interface
Create a Maven module
seata-dubbo-apiand define the service interface:
<code>public interface StockApi {
/**
* 库存扣减
*/
boolean deduct(String productCode, int count);
}</code>After the interface is ready, we will create the stock service and order service.
2.2 Create Stock Service
Create a Spring Boot project
seata-dubbo-stockand add the following dependencies in
pom.xml:
<code><properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<!-- SpringBoot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MySQL driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!-- Nacos discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- Seata distributed transaction component -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!-- API module dependency -->
<dependency>
<groupId>com.example</groupId>
<artifactId>seata-dubbo-api</artifactId>
<version>3.0-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement></code>Add
application.propertieswith the following configuration:
<code>spring.application.name=seata-dubbo-stock
server.port=9002
# Data source
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seata-stock
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# MyBatis
mybatis.config-location=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
# Nacos discovery
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# Dubbo settings
dubbo.scan.base-packages=com.example.cloud.nacos.dubbo.seata
dubbo.protocol.name=dubbo
dubbo.protocol.port=-1
dubbo.registry.address=nacos://${spring.cloud.nacos.discovery.server-addr}
# Seata settings
seata.application-id=seata-dubbo-stock
seata.tx-service-group=my_test_tx_group
seata.service.vgroup-mapping.my_test_tx_group=default
seata.service.grouplist.default=127.0.0.1:8091</code>Implement the Dubbo service:
<code>@com.alibaba.dubbo.config.annotation.Service
public class StockApiImpl implements StockApi {
@Autowired
private StockService stockService;
@Override
public boolean deduct(String productCode, int count) {
try {
stockService.deduct(productCode, count);
return true;
} catch (Exception e) {
return false;
}
}
}</code>Start the service with
@EnableDiscoveryClientand a main class.
<code>@EnableDiscoveryClient
@MapperScan("com.example.cloud.nacos.dubbo.seata")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}</code>2.3 Create Order Service
Create another Spring Boot project
seata-dubbo-orderwith the same dependencies and configuration as the stock service. Replace the HTTP‑based remote call with a Dubbo reference:
<code>@Component
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@com.alibaba.dubbo.config.annotation.Reference
private StockApi stockApi;
@GlobalTransactional
public void create(String userId, String productCode, int orderCount) throws Exception {
// Remote stock deduction via Dubbo
stockApi.deduct(productCode, orderCount);
Order order = new Order();
order.setUserId(userId);
order.setProductCode(productCode);
order.setCount(orderCount);
order.setMoney(orderCount * 100);
orderMapper.insert(order);
}
}</code>Corresponding
application.propertiesmirrors the stock service configuration, with its own service name and port.
2.4 Service Testing
Two test cases are performed:
Distributed transaction normal commit – calling
http://127.0.0.1:9001/order/create?userId=张三&productCode=wahaha&orderCount=1should deduct stock and insert an order.
Distributed transaction exception rollback – modify
OrderService.create()to throw an exception after the order is created, then verify that both stock and order tables remain unchanged.
Database snapshots and Seata TC server logs confirm the expected behavior in both scenarios.
Seata Service Address Configuration
When the number of Seata TC servers grows, hard‑coding their addresses becomes unwieldy. By registering the Seata server in a service registry (e.g., Nacos), clients can discover the address dynamically.
3.1 Seata Server Configuration
Copy the
store.registrysection from
conf/application.example.ymlto
conf/application.ymland restart the Seata TC server. The server will appear in the Nacos console.
3.2 Seata Client Configuration
In the client’s
application.propertiesadd:
<code># Seata identifiers
seata.application-id=seata-dubbo-stock
seata.tx-service-group=my_test_tx_group
seata.service.vgroup-mapping.my_test_tx_group=default
# Registry type
seata.registry.type=nacos
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.group=SEATA_GROUP</code>Restart the services; the client now obtains the TC server address from Nacos.
3.3 Error Troubleshooting
If an error such as version incompatibility appears, verify that the Seata client version matches the server version. For example, upgrading
spring-cloud-alibabafrom
2.2.1.RELEASEto
2.2.3.RELEASEbrings the embedded Seata client to
1.3.0, which is compatible with a server version
1.5.2.
Conclusion
This article demonstrated how to integrate Dubbo with Seata to achieve distributed transaction management in a Spring Boot microservice environment, covering service creation, configuration, testing, dynamic address discovery, and common pitfalls.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.