You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Axon框架抛出LockAcquisitionFailedException异常且尝试次数为100问题咨询

Axon Framework 锁获取失败问题排查与解决

最大尝试次数为100的原因

你使用的Axon 4.3.3属于4.4之前的旧版本,PessimisticLockFactory 在4.4.0版本之前的默认最大尝试次数就是100,4.4.0版本之后官方才将默认值调整为Integer.MAX_VALUE(即2147483647),这就是你和其他用户报错数值不同的核心原因,和自定义配置无关。

锁超时问题根因

虽然你的命令处理逻辑没有重计算,但聚合根锁的持有周期是从聚合加载开始,到所有产生的事件全部持久化完成才释放,以下场景会拉长锁持有时间导致竞争失败:

  • 聚合生命周期长达数年,即使配置了200次事件触发快照,每次加载聚合时仍需要加载快照生成后产生的所有事件,事件数量越多加载耗时越长
  • 事件溯源Handler中存在连锁事件触发逻辑:SeatFreedEvent 处理时如果预约队列不为空会触发SeatReservedEvent,如果队列长度较大,单次锁持有周期内会连续处理大量事件,大幅拉长锁占用时间
  • 事件存储查询性能不足,没有为事件表的aggregateIdentifier、sequenceNumber字段建立联合索引,导致聚合事件查询过慢

解决方案

  • 调整锁配置:如果暂时无法升级Axon版本,可手动自定义LockFactory Bean,调高最大尝试次数,示例配置如下:
@Bean
public LockFactory lockFactory() {
    return PessimisticLockFactory.builder()
            .maxAttempts(Integer.MAX_VALUE)
            .acquireAttemptsPerSecond(10) // 调整锁尝试频率,避免CPU空转
            .queueSizeThreshold(10000)
            .build();
}
  • 优化快照策略:将快照触发阈值从200调低到50~100,减少每次聚合加载时需要回溯的事件数量,缩短锁持有时间
  • 优化连锁事件逻辑:预约队列的批量消费逻辑不要放在单次聚合事件处理中执行,可引入Saga组件分批处理队列元素,避免单次锁持有周期过长
  • 优化事件存储性能:给事件存储表的aggregateIdentifier、sequenceNumber字段添加联合索引,提升聚合事件查询速度
  • 聚合拆分:如果该聚合并发访问量持续升高,可将预约队列等高频变更逻辑拆分为独立子聚合,缩小锁竞争的范围

聚合根锁监控方法

  • 自定义带监控能力的LockFactory装饰器,在锁获取、释放节点埋点,采集锁持有时间、等待队列长度、获取失败次数等指标,上报到监控系统,示例代码如下:
public class MonitoredLockFactory implements LockFactory {
    private final LockFactory delegate;
    private final MeterRegistry meterRegistry;

    public MonitoredLockFactory(LockFactory delegate, MeterRegistry meterRegistry) {
        this.delegate = delegate;
        this.meterRegistry = meterRegistry;
    }

    @Override
    public Lock obtain(String aggregateIdentifier) {
        Timer.Sample waitSample = Timer.start(meterRegistry);
        Lock delegateLock = delegate.obtain(aggregateIdentifier);
        waitSample.stop(meterRegistry.timer("axon.lock.wait.time", "aggregateId", aggregateIdentifier));
        
        return () -> {
            Timer.Sample holdSample = Timer.start(meterRegistry);
            try {
                delegateLock.release();
            } finally {
                holdSample.stop(meterRegistry.timer("axon.lock.hold.time", "aggregateId", aggregateIdentifier));
            }
        };
    }
}
  • 对LockAcquisitionFailedException异常的发生次数配置监控告警,出现异常时及时触发通知

定位锁持有进程的方法

  • 开启Axon框架的debug级日志,在日志格式中添加线程ID、实例IP字段,出现锁超时时根据报错的聚合ID搜索日志,找到上一个成功获取该聚合锁的线程ID和对应实例
  • 导出对应实例的JVM线程栈,根据线程ID查找线程当前的执行栈,即可定位到线程卡住的具体逻辑(常见为事件存储IO阻塞、死锁、第三方调用阻塞等)
  • 如果是多实例部署,可在锁埋点日志中增加实例标识,优先定位到锁被哪个实例持有,再排查对应实例的运行状态

内容的提问来源于stack exchange,提问作者Nader Kahwaji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 04:06:00