我们正在实现一个 REST API,它将启动多个长时间运行的后端任务。我一直在阅读 RESTful Web Services Cookbook,建议返回 HTTP 202 / Accepted,并带有指向正在处理的任务的 Content-Location 标头。 (例如。http://www.example.org/orders/tasks/1234 http://www.example.org/orders/tasks/1234),并让客户端轮询此 URI 以获取长时间运行任务的更新。
这个想法是让 REST API 立即将消息发布到队列,后台工作角色从队列中拾取消息并启动多个后端任务(也使用队列)。我发现这种方法的问题是如何为任务分配唯一的 ID,然后让客户端通过向 Content-Location URI 发出 GET 来请求任务的状态。
如果 REST API 立即发布到队列,那么它可以生成一个 GUID 并将其作为属性附加到要添加到队列的消息上,但获取请求的状态会变得很尴尬。
另一种选择是让 REST API 立即向数据库添加一个条目(比方说一个带有新订单 ID 的订单),并具有初始状态,然后将一条消息放入队列中以启动后台任务,随后将更新该数据库记录。 API 将在 Content-Location 标头的 URI 中返回这个新订单 ID,供客户端在检查任务状态时使用。
不知何故,首先添加数据库条目,然后将消息添加到队列似乎是倒退的,但仅将请求添加到队列使得很难跟踪进度。
推荐的方法是什么?
非常感谢您的见解。
我假设您的系统如下所示。您有一个 REST 服务,它接收来自客户端的请求。它将请求转换为业务逻辑可以理解的命令。您将这些命令放入队列中。您有一个或多个工作线程可以处理这些命令并从队列中删除这些命令,并将结果发送到 REST 服务,该服务可以响应客户端。
您的问题是,由于长时间运行的任务,客户端连接超时,因此您无法发送响应。因此,您可以做的就是在将命令放入队列并添加轮询链接后发送 202 Accepted,以便客户端能够轮询更改。您的任务有多个子任务,因此有进度,而不仅仅是待处理和完成的状态更改。
- 如果您想坚持轮询,则应该创建一个新的 REST 资源,其中包含长期运行任务的实际状态和进度。这意味着您必须将此信息存储在数据库中,以便 REST 服务能够响应如下请求
GET /tasks/23461/status
。这意味着您的工作人员在完成子任务或整个任务时必须更新数据库。
- 如果您的 REST 服务作为守护进程运行,那么您可以通过进度通知它,因此将任务状态存储在数据库中将不是工作人员的责任。这种REST服务也可以将信息存储在内存中。
- 如果您决定使用 websockets 来通知客户端,那么您可以创建一个通知服务。通过 REST,您必须使用任务 ID 进行响应。之后,您在 websocket 连接上发回此任务 id,以便通知服务知道哪个 websocket 连接订阅了某个任务的事件。之后就不需要REST服务了,只要客户端不关闭连接就可以通过websocket连接发送进度。
- 您可以通过以下方式组合这些解决方案。您让 REST 服务创建任务资源,以便您能够使用轮询链接访问进度。之后,您发送回一个带有 202 的标识符,您可以通过 websockets 连接发送回该标识符。所以你可以使用通知服务来通知客户端。随着进度,您的工作人员将通知 REST 服务,该服务将创建一个类似的链接
GET /tasks/23461/status
并通过通知服务将该链接发送给客户端。之后客户端可以使用该链接来更新其状态。
我认为如果您的 REST 服务作为守护进程运行,最后一个是最好的解决方案。这是因为您可以将通知职责转移到专用的通知服务,该服务可以使用 websockets、轮询、SSE,无论您想要什么。它可以在不终止 REST 服务的情况下崩溃,因此 REST 服务将保持稳定和快速。如果您也使用 202 发回手动更新链接,则客户端可以进行手动更新(假设是人工控制的客户端),因此如果通知服务不可用,您将遇到类似优雅降级的情况。您不必维护通知服务,因为它不知道有关任务的任何信息,它只会将数据发送到客户端。您的工作人员无需了解有关如何发送通知以及如何创建超链接的任何信息。维护客户端代码也会更容易,因为它几乎是一个纯粹的 REST 客户端。唯一的额外功能是通知链接的订阅,该链接不会经常更改。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)