高TPS下CompletableFuture严格序文档处理方案咨询
高并发场景下文档有序处理的技术方案咨询
核心业务约束
- 不同文档之间的处理顺序无要求,可完全并行执行
- 同一文档的多次更新操作必须严格按照顺序串行执行,不允许出现乱序并发
- 系统整体TPS较高,方案必须满足高性能、低延迟要求,处理耗时不能超出约定SLA
已尝试的实现方案及存在的问题
当前初始设计采用ConcurrentHashMap<documentId, ReentrantLock>结构实现文档级锁:每个文档对应唯一ID,同一文档的所有更新请求都关联同一个锁实例。基于该结构先后尝试了两种具体实现,均存在明显缺陷:
方案1:CompletableFuture异步链式执行
实现代码如下:
void process(doc) { String id = doc.getId(); CompletableFuture.runAsync(() -> {getLock();}, executor) .thenRunAsync(() -> { doSomeWork();}, executor) .whenComplete((ignored, err) -> { removeFromLockMap(id); handleErrors(err); }, executor).join() }
该方案的核心缺陷:ReentrantLock的锁语义要求加锁、解锁操作必须由同一个线程完成,但上述链式异步调用中,加锁、业务处理、解锁三个步骤会被线程池调度到不同工作线程执行,完全不符合ReentrantLock的使用要求,会出现锁泄漏、并发访问冲突等严重问题。
方案2:单线程池数组轮询分配
- 实现逻辑:预先创建一批SingleThreadPool类型的执行器数组,借助Guava的
Iterators.cycle()构造环形迭代器,每次收到处理请求时轮询分配一个单线程执行器,保证单次process流程内的所有异步调用都运行在同一个线程上,以此满足ReentrantLock的线程绑定要求。 - 存在的缺陷:
- 资源开销过高:为支撑日常业务处理需要配置至少1000个单线程执行器,流量峰值时甚至需要扩容到1500个,线程、内存资源浪费严重,扩展性极差
- 延迟无法达标:单线程执行器同一时间只能处理一个任务,后续提交到同一个执行器的任务会进入队列排队等待,流量高峰时的排队耗时会直接导致处理延迟超出SLA要求
待解决问题
如何设计可行的技术方案,在满足低延迟SLA要求的前提下,同时保证同一文档的操作严格按序处理?
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

