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

Spring+CMIS构建的REST服务并发调用时ID重复问题求助

解决Alfresco CMIS服务并发创建节点时ID重复的问题

你这明显踩了并发场景下「读-改-写」操作的经典坑——你的ID生成逻辑完全不是线程安全的,Thread.sleep这种方式只是延缓了问题,根本解决不了核心的竞态条件。

问题根源分析

你的流程是:查询Alfresco最后一个节点ID → 加1生成新ID → 创建新节点。在并发调用时,多个线程会同时执行「查询最后ID」这一步,它们拿到的是同一个值,后续自然都会生成相同的新ID,最终导致重复创建。哪怕你加了sleep,只要多个线程在同一时间窗口内完成查询操作,重复问题依然会出现。

可行的解决方案

1. 利用Alfresco/CMIS的原子性特性(优先推荐)

Alfresco本身支持事务和锁机制,我们可以把ID生成和节点创建变成原子操作:

  • 方案1a:让Alfresco自动生成唯一ID
    如果业务允许放弃自定义递增ID,直接用CMIS默认的自动生成ID功能,完全不用自己操心并发问题。创建节点时不指定ID,CMIS会自动分配全局唯一的ID。
  • 方案1b:用Alfresco计数器节点+锁
    在Alfresco中创建一个专门的「ID计数器」节点,每次生成新ID时:
    1. 先锁定这个计数器节点(CMIS的LockService可以实现)
    2. 读取当前计数器的值,加1
    3. 更新计数器节点的属性
    4. 释放锁,用新生成的ID创建业务节点
      这个流程确保同一时间只有一个线程能修改计数器,彻底避免竞态。

2. 应用层加分布式锁

如果不想修改Alfresco的配置,可以在你的Spring服务中引入分布式锁,把ID生成+节点创建的整个流程变成串行化操作:
比如用Redis的Redisson实现分布式锁,伪代码如下:

@Autowired
private RedissonClient redissonClient;

public void createNodeWithUniqueId() {
    // 定义锁的名称,确保全局唯一
    RLock lock = redissonClient.getLock("alfresco-node-id-generator-lock");
    try {
        // 阻塞等待获取锁,直到拿到为止
        lock.lock();
        
        // 这里执行你的原有逻辑:查询最后ID→生成新ID→创建节点
        String lastId = getLastNodeIdFromAlfresco();
        String newId = String.valueOf(Integer.parseInt(lastId) + 1);
        createNodeInAlfresco(newId);
    } finally {
        // 无论操作成功失败,都要释放锁
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

这样同一时间只有一个线程能进入ID生成和节点创建的逻辑,从根源上杜绝重复。

3. 数据库原子生成递增ID

如果你的服务有配套数据库,可以在数据库层实现原子性的ID生成:

  1. 先创建一个计数器表:
CREATE TABLE alfresco_node_counter (
    id INT PRIMARY KEY AUTO_INCREMENT,
    current_max_id INT NOT NULL DEFAULT 0
);
-- 初始化一条默认记录
INSERT INTO alfresco_node_counter (current_max_id) VALUES (0);
  1. 在Spring服务中用事务+悲观锁实现原子递增:
@Autowired
private NodeCounterRepository counterRepo;

@Transactional
public String generateNextId() {
    // 锁定唯一的计数器记录,确保原子操作
    NodeCounter counter = counterRepo.findById(1L).orElseThrow(() -> new RuntimeException("计数器未初始化"));
    int nextId = counter.getCurrentMaxId() + 1;
    counter.setCurrentMaxId(nextId);
    counterRepo.save(counter);
    return String.valueOf(nextId);
}

拿到这个ID后再去Alfresco创建节点,数据库的事务和锁机制会保证ID不会重复。

4. 改用UUID(最简单无侵入)

如果业务不需要连续递增的ID,直接用UUID作为节点ID是最省心的方案:

String uniqueId = UUID.randomUUID().toString();
createNodeInAlfresco(uniqueId);

UUID本身是全局唯一的,完全不用考虑并发冲突,代码改动也最小。

总结

Thread.sleep这种方式本质上是「掩耳盗铃」,根本解决不了并发竞争问题。优先推荐用Alfresco本身的原子机制或者分布式锁,这两种方案最贴合你的业务场景;如果业务允许,UUID方案是最快落地的。

内容的提问来源于stack exchange,提问作者Héctor Mancilla

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:48:01