Long 运行 REST API with queues

Long running REST API with queues

我们正在实施 REST API,它将启动多个长 运行 后端任务。我一直在阅读 RESTful Web Services Cookbook,建议 return HTTP 202 / Accepted with a Content-Location header 指向正在处理的任务。 (例如 http://www.example.org/orders/tasks/1234),并让客户端轮询此 URI 以获取长 运行 任务的更新。

想法是让 REST API 立即 post 向 queue 发送消息,后台工作人员角色从 queue 接收消息,并且启动多个后端任务,也使用 queues。我看到这种方法的问题是如何为任务分配一个唯一的 ID,然后让客户端通过向 Content-Location URI 发出 GET 来请求任务的状态。

如果 REST API 立即 post 发送到 queue,那么它可以生成一个 GUID 并将其作为属性附加到要添加到 [=39 的消息中=],但是获取请求的状态变得很尴尬。

另一种选择是让 REST API 立即向数据库添加一个条目(假设是一个订单,带有一个新的订单 ID),具有初始状态,然后随后在queue 启动后台任务,随后将更新该数据库记录。 API 将 return 这个新的订单 ID 放在 Content-Location header 的 URI 中,供客户端在检查任务状态时使用。

不知何故先添加数据库条目,然后将消息添加到 queue 似乎是倒退,但仅将请求添加到 queue 很难跟踪进度。

推荐的方法是什么?

非常感谢您的见解。

我假设您的系统如下所示。您有一个 REST 服务,它接收来自客户端的请求。它将请求转换为业务逻辑可以理解的命令。您将这些命令放入队列中。您有一个或多个工作人员可以处理这些命令并从队列中删除这些命令,并将结果发送到 REST 服务,该服务可以响应客户端。

您的问题是您的 运行 长任务导致客户端连接超时,因此您无法发送响应。因此,您可以做的是在将命令放入队列并添加轮询 link 后发送 202 accepted,以便客户端能够轮询更改。您的任务有多个子任务,因此有进展,而不仅仅是待定和完成状态更改。

  1. 如果您想坚持轮询,您应该创建一个新的 REST 资源,其中包含实际状态和长 运行 任务的进度。这意味着您必须将此信息存储在数据库中,以便 REST 服务能够响应 GET /tasks/23461/status 之类的请求。这意味着您的工作人员必须在完成子任务或整个任务时更新数据库。
  2. 如果您的 REST 服务 运行 作为守护进程,那么您可以通过进度通知它,因此将任务状态存储在数据库中将不是工作人员的责任。这种REST服务也可以将信息存储在内存中。
  3. 如果您决定使用 websockets 来通知客户端,那么您可以创建一个通知服务。通过 REST,您必须使用任务 ID 进行响应。之后你在 websocket 连接上发回这个任务 id,这样通知服务就会知道哪个 websocket 连接订阅了某个任务的事件。之后就不需要REST服务了,只要客户端不关闭连接就可以通过websocket连接发送进度。
  4. 您可以通过以下方式组合这些解决方案。您让 REST 服务创建任务资源,这样您就可以使用轮询 link 来访问进度。之后,您发回一个带有 202 的标识符,您通过 websockets 连接发回该标识符。所以你可以使用一个通知服务来通知客户端。随着进度,您的工作人员将通知 REST 服务,该服务将创建一个 link 类似 GET /tasks/23461/status 并通过通知服务将 link 发送给客户端。之后客户端可以使用 link 更新其状态。

如果您的 REST 服务作为守护进程运行,我认为最后一个是最佳解决方案。这是因为您可以将通知责任移至专用通知服务,该服务可以使用 websockets、轮询、SSE,随心所欲。它可以在不杀死 REST 服务的情况下崩溃,因此 REST 服务将保持稳定和快速。如果您也使用 202 发回手动更新 link,则客户端可以进行手动更新(假设是人为控制的客户端),因此如果通知服务不可用,您将遇到类似优雅降级的情况。您不必维护通知服务,因为它对任务一无所知,它只会向客户端发送数据。您的工作人员无需知道如何发送通知以及如何创建 hyperlinks。维护客户端代码也会更容易,因为它几乎是一个纯 REST 客户端。唯一的额外功能是订阅通知 link,它不会经常更改。