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时:- 先锁定这个计数器节点(CMIS的
LockService可以实现) - 读取当前计数器的值,加1
- 更新计数器节点的属性
- 释放锁,用新生成的ID创建业务节点
这个流程确保同一时间只有一个线程能修改计数器,彻底避免竞态。
- 先锁定这个计数器节点(CMIS的
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生成:
- 先创建一个计数器表:
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);
- 在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
相关产品推荐
相关产品推荐

