datai/datai-scenes/datai-scene-salesforce/docs/decisions/2026-02-03-003-04-ADR-异步操作轮询策略.md

3.4 KiB
Raw Permalink Blame History

ADR-003-04: 异步操作轮询策略

状态

已接受

日期

2026-02-03

背景

Salesforce Metadata API 的许多关键操作(如 deploy, retrieve是异步执行的。API 调用立即返回一个 AsyncResult,其中包含 id 和初始状态。客户端必须后续通过此 ID 查询最终结果。 我们需要为 datai-scene-salesforce 模块设计一种通用的异步操作管理机制,以支持上层业务(如部署、检索)获取操作结果。 核心问题是:如何在服务端高效且简单地获取异步操作的最终状态?

决策

选择 服务端同步轮询 (Server-side Synchronous Polling) 方案。

具体实现为:

  1. Service 层封装:在 MetadataAsyncService 中提供 pollAsyncResult(String asyncId, long interval, long timeout) 方法。
  2. 阻塞式实现:使用 Thread.sleep(interval) 在循环中检查状态,直到状态变为 CompletedFailed 或超时。
  3. 对外接口
    • 提供 GET /poll/{id} 接口,允许客户端发起一个长连接请求等待结果(受超时限制)。
    • 同时提供 GET /status/{id} 接口,供不希望阻塞的客户端手动轮询。

理由如下

  1. 简单可靠:当前项目架构为基于 Spring Boot 的 Web 应用,使用线程阻塞虽然占用资源,但在 Metadata API 操作并发量不大的场景下(通常是管理员操作),是实现成本最低且最可靠的方案。
  2. 易于封装:将轮询逻辑封装在服务端,可以简化上层业务代码。例如,部署服务可以直接调用 asyncService.pollAsyncResult(...) 并在方法返回后立即进行后续处理(如更新部署记录),而无需编写复杂的异步回调逻辑。
  3. 超时控制:服务端统一控制超时和重试间隔,防止客户端滥用 API 或因网络问题导致的轮询失控。

后果

正面影响

  1. 简化客户端逻辑:客户端(前端或第三方调用者)可以选择简单的“等待结果”模式,减少了处理轮询定时器的复杂度。
  2. 统一管理:轮询频率和超时策略由服务端统一配置,易于优化和调整。
  3. 开发效率:利用现有的同步编程模型,开发调试迅速。

负面影响

  1. 资源占用:每个轮询请求会占用一个 Tomcat 线程。如果并发量激增,可能导致线程池耗尽(但在 Metadata 操作场景下风险较低)。
  2. 响应延迟:受限于轮询间隔(如 5秒获取结果的延迟至少为 interval 时间,不如 WebSocket 实时。

替代方案

方案 2客户端轮询 (Client-side Polling)

  • 描述:服务端只提供 checkStatus 接口,客户端负责定时调用。
  • 优点:服务端无状态,不占用线程等待,扩展性好。
  • 缺点:如果服务端其他模块(如部署后自动运行测试)依赖该结果,则服务端仍需实现轮询逻辑,导致逻辑重复。

方案 3响应式/异步非阻塞 (Reactive/Async)

  • 描述:使用 Spring WebFlux 或 @Async + CompletableFuture
  • 优点:非阻塞,资源利用率高。
  • 缺点:引入了响应式编程的复杂度,与当前项目的传统 Servlet 架构风格不一致,增加了维护成本。

相关文档