Spring Boot 3.3 + Atomikos 分布式事务日志路径配置踩坑记录

问题背景

报错: The specified log seems to be in use already: tmlog in ./. Make sure that no other instance is running, or kill any pending process if needed.

在使用 Spring Boot 3.3 + Atomikos 实现分布式事务时,发现事务日志文件(tmlog)总是生成在项目根目录,而不是期望的模块目录下。多个模块的 tmlog 文件混在一起,难以区分和管理。

在多模块的情况下,因为tmlog默认都是在父级模块下,所以每个模块都是共用一个tmlog,因为共用一个tmlog所以所有模块在同一时间只能启动一个,不能同时运行,但是部署在服务器不存在这个问题,因为在服务器上部署时每个模块的jar包不在想他目录,所以不会出现这个问题,主要解决的就是开发环境想要同时启动多个模块的问题,方便调试

环境信息

  • JDK: 21
  • Spring Boot: 3.3.0
  • Atomikos: 6.0.0 (transactions-spring-boot3-starter)
  • 项目结构: Maven 多模块

问题现象

默认配置下,所有模块的事务日志都输出到项目根目录:

shell 复制代码
dog-auto-servers/
├── tmlog.lck
├── tmlog0.log
├── tmlog1.log
└── task-formula-ai/
└── task-auto-test/

错误尝试

方式一:自定义 TransactionManagerConfig Bean

最初尝试通过 Java 配置类手动创建 Bean:

java 复制代码
@Configuration
public class TransactionManagerConfig {

    @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
    public UserTransactionManager atomikosTransactionManager() throws Throwable {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        userTransactionManager.setForceShutdown(false);
        // ❌ 这样设置无效!
        return userTransactionManager;
    }

    @Bean(name = "transactionManager")
    public JtaTransactionManager transactionManager() throws Throwable {
        return new JtaTransactionManager(userTransaction(), atomikosTransactionManager());
    }
}

结果:YAML 中的配置完全不生效,日志仍在根目录。

方式二:在 YAML 中配置但保留自定义 Bean

yaml 复制代码
spring:
  jta:
    atomikos:
      properties:
        log-base-dir: ./logs/atomikos
        log-base-name: task-formula-ai-tmlog
        enable-logging: true
        tm-unique-name: task-formula-ai
java 复制代码
@Configuration
public class TransactionManagerConfig {

    @Bean(name = "atomikosTransactionManager")
    public UserTransactionManager atomikosTransactionManager() throws Throwable {
        // ❌ 仍然无效!因为自定义 Bean 覆盖了自动配置
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        return userTransactionManager;
    }
}

结果 :配置依然不生效。原因:自定义 Bean 会覆盖 Spring Boot 的自动配置

正确解决方案

✅ 方案:删除自定义配置类,使用 Spring Boot 自动配置

1. 删除 TransactionManagerConfig.java

直接删除自定义的配置类文件:

TransactionManagerConfig.java

2. 在 application.yml 中配置
yaml 复制代码
spring:
  application:
    name: task-formula-ai  # 服务名称
  jta:
    atomikos:
      properties:
        log-base-dir: ./logs/atomikos           # 事务日志目录(相对路径)
        log-base-name: ${spring.application.name}-tmlog  # 日志文件基础名称
        enable-logging: true                    # 启用磁盘日志
        tm-unique-name: ${spring.application.name}       # 事务管理器唯一标识
        max-actives: 10000                      # 最大活动事务数
        default-jta-timeout: 300000             # 默认事务超时时间(毫秒)
        force-shutdown-on-vm-exit: false        # VM 关闭时是否强制关闭
3. 多模块配置示例

为每个模块设置不同的服务名称,日志文件就会自动区分开:

task-formula-ai:

yaml 复制代码
spring:
  application:
    name: task-formula-ai

task-auto-test:

yaml 复制代码
spring:
  application:
    name: task-auto-test
4. 重启验证

清理并重启应用后,日志文件结构变为:

shell 复制代码
dog-auto-servers/
├── task-formula-ai/
│   └── logs/
│       └── atomikos/
│           ├── task-formula-ai-tmlog.lck
│           └── task-formula-ai-tmlog0.log
└── task-auto-test/
    └── logs/
        └── atomikos/
            ├── task-auto-test-tmlog.lck
            └── task-auto-test-tmlog0.log

核心原理

为什么自定义 Bean 会导致配置失效?

Spring Boot 的自动配置类 AtomikosJtaConfiguration 使用了条件注解:

java 复制代码
@Configuration
@ConditionalOnMissingBean(UserTransactionManager.class)
public class AtomikosJtaConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "spring.jta.atomikos.properties")
    public UserTransactionManager userTransactionManager() {
        // 只有没有自定义 Bean 时才会创建
        // 这里会自动读取 YAML 配置
    }
}

当你手动定义了 UserTransactionManager Bean 后:

  • @ConditionalOnMissingBean 条件不满足
  • 自动配置类不会生效
  • YAML 中的 spring.jta.atomikos.properties.* 配置不会被读取
  • Atomikos 使用默认配置(日志输出到当前目录)

Atomikos 配置属性映射

YAML 属性 Atomikos 系统属性 说明
log-base-dir com.atomikos.icatch.log_base_dir 日志目录
log-base-name com.atomikos.icatch.log_base_name 日志文件基础名称
tm-unique-name com.atomikos.icatch.tm_unique_name 事务管理器唯一标识
enable-logging com.atomikos.icatch.enable_logging 是否启用日志
max-actives com.atomikos.icatch.max_actives 最大活动事务数
default-jta-timeout com.atomikos.icatch.default_jta_timeout 默认事务超时

如果必须使用自定义 Bean 怎么办?

某些场景下可能需要自定义 Bean(比如添加额外的初始化逻辑),这时可以通过设置系统属性来配置:

java 复制代码
@Configuration
public class TransactionManagerConfig {

    @Value("${spring.jta.atomikos.properties.log-base-dir:./logs/atomikos}")
    private String logDir;

    @Value("${spring.application.name:app}")
    private String appName;

    @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
    public UserTransactionManager atomikosTransactionManager() throws Throwable {
        // ⚠️ 必须在 init() 调用之前设置系统属性
        System.setProperty("com.atomikos.icatch.log_base_dir", logDir);
        System.setProperty("com.atomikos.icatch.tm_unique_name", appName);
        System.setProperty("com.atomikos.icatch.log_base_name", appName + "-tmlog");
        System.setProperty("com.atomikos.icatch.enable_logging", "true");

        UserTransactionManager userTransactionManager = new UserTransactionManager();
        userTransactionManager.setForceShutdown(false);
        userTransactionManager.init();
        return userTransactionManager;
    }
}

最佳实践建议

  1. 优先使用 Spring Boot 自动配置
    • 遵循约定优于配置原则
    • 减少自定义代码
    • 配置更直观、易维护
  2. 多模块项目务必设置唯一的服务名称
yaml 复制代码
spring:
  application:
    name: ${project.artifactId}  # 或使用具体名称

使用相对路径配置日志目录

  1. log-base-dir: ./logs/atomikos # 相对于工作目录

生产环境使用绝对路径

  1. log-base-dir: /var/logs/${spring.application.name}/atomikos
  2. 确保每个模块的 tm-unique-name唯一
    • 避免多个实例冲突
    • 便于事务恢复和故障排查

验证清单

  • 删除了自定义的 TransactionManagerConfig 配置类
  • application.yml 中正确配置了 spring.jta.atomikos.properties.*
  • 为每个模块设置了不同的 spring.application.name
  • 清理了旧的 tmlog 文件
  • 重启应用后检查日志目录是否正确生成
  • 验证事务功能是否正常

常见问题 FAQ

Q1: 配置后仍然在根目录生成 tmlog 怎么办?

A: 检查以下几点:

  1. 确认已删除所有自定义的 TransactionManagerConfig
  2. 清理 Maven 缓存:mvn clean
  3. 删除旧的 tmlog 文件
  4. 完全重启应用(不是热重载)

Q2: 多个模块可以共享同一个事务管理器吗?

A: 不建议。每个模块应该有自己独立的事务管理器,通过设置不同的 tm-unique-name 来区分。

Q3: 可以使用环境变量动态配置吗?

A: 可以,支持环境变量替换:

log-base-dir: ${ATOMIKOS_LOG_DIR:./logs/atomikos}

Q4: 生产环境如何配置?

A: 在生产环境配置文件中指定绝对路径:

yaml 复制代码
# application-prod.yml
spring:
  jta:
    atomikos:
      properties:
        log-base-dir: /data/logs/${spring.application.name}/atomikos
        enable-logging: true

总结

Spring Boot 的自动配置机制大大简化了 Atomikos 的配置,但前提是不要手动覆盖自动配置的 Bean。当遇到配置不生效的问题时,首先检查是否有自定义 Bean 覆盖了自动配置。

核心要点

  • ❌ 自定义 UserTransactionManager Bean → YAML 配置失效
  • ✅ 删除自定义 Bean → YAML 配置生效
  • 🔧 必须自定义时 → 通过 System.setProperty() 设置

希望这篇文章能帮助你少走弯路!🎉

相关推荐
我爱学习好爱好爱2 小时前
Ansible 自动化部署全栈项目(Spring Boot + Vue + MySQL + Redis)实战(Rockylinux9.6)
spring boot·自动化·ansible
snakeshe10102 小时前
MyBatis 从入门到实践:ORM 核心机制与动态 SQL 全解析
后端
Data 实验室2 小时前
TaskPyro “小龙虾版本”专业爬虫管理平台来了:AI+分布式+IM 机器人,一套搞定企业级爬虫调度
人工智能·分布式·爬虫
野犬寒鸦2 小时前
高并发利器:SingleFlight优化指南(Java版实现与项目实战)
服务器·开发语言·redis·后端·面试
gelald2 小时前
JVM - 类加载机制
java·jvm·后端
想你依然心痛2 小时前
HarmonyOS 5.0教育行业解决方案:基于分布式能力的沉浸式智慧课堂系统
分布式·wpf·harmonyos
weixin_449190412 小时前
golang中int8溢出
开发语言·后端·golang
Darkdreams2 小时前
Java进阶-在Ubuntu上部署SpringBoot应用
java·spring boot·ubuntu
清汤饺子2 小时前
Everything Claude Code:让我把 AI 编程效率再翻一倍的东西
前端·javascript·后端