【问题标题】:RESTful APIs must be stateless, but what about concurrency?RESTful API 必须是无状态的,但是并发呢?
【发布时间】:2012-04-08 09:52:07
【问题描述】:

我很好奇如何解决 RESTful API 的并发问题。更具体地说,我有一组需要手动检查和更新的对象,例如需要手动更新列的行数;但是,如果我向多个客户端开放 API,他们都会从上到下抓取这些项目,因此许多用户将同时填充同一行的列。我宁愿没有冲突,简单、有状态的方法是将项目转储到服务上的队列中,并在人们请求时将它们弹出。

什么是无状态版本?通过IP地址散列,还是根据id随机抓取行?

:: 更新 ::

“嗯,所以从客户端的角度来看,它一定是无状态的?

这当然很有意义。我刚刚在阅读一篇关于 RESTful API 的文章 (ibm.com/developerworks/webservices/library/ws-restful),在遇到关于分页的一些信息后,我担心我的状态队列类似于按一页递增,但是它们实际上完全不同,因为“下一页”在客户端是相对的,而“弹出”对于客户端始终是无状态的:之前弹出的内容无关紧要。

谢谢你让我清醒!” -我

【问题讨论】:

  • 我在这里没有真正看到问题/问题。 RESTful API 可以——而且 IME 几乎总是——由有状态服务器支持。您能否说明您要解决的问题?
  • 可以使用电子标签提供并发
  • Hrm,所以从客户端的角度来看它必须是无状态的?这当然很有意义。我刚刚读了一篇关于 RESTful API 的文章 (ibm.com/developerworks/webservices/library/ws-restful),在遇到了关于分页的一点点之后,我担心我的状态队列类似于递增一页,但它们实际上与“下一页”完全不同" 在客户端是相对的,而 "pop" 对于客户端始终是无状态的。之前弹出的内容无关紧要。谢谢你让我清醒!

标签: api rest concurrency stateless


【解决方案1】:

您可以采取两种基本方法:

  1. 完全无状态,并采用“最后请求获胜”策略。尽管听起来很奇怪,但就可预测性、可扩展性、代码复杂性以及客户端和服务器端的实现而言,它可能是最简洁的解决方案。它也有很多优先级:看看像谷歌这样的网站如何使用start=10 用于第 2 页、start=20 用于第 3 页等的查询分页。

    当您在页面之间来回导航时,您可能会发现页面内的内容会发生变化,但那又如何呢?您始终可以获取最新信息,而且 Google 可以在其众多服务器中的任何一个服务器上处理您的请求,而无需查找您的会话信息来确定您最后的查询上下文是什么。

    这种方法的最大优势是服务器实现的简单性。每个请求都可以直接传递到后端的数据层,并且在 HTTP 级别(通过 E-Tags 或 Last-Modified 标头)和服务器端(使用 memcache 之类的东西)缓存绝对成熟,例如示例)。

  2. 进入有状态,并想办法让您的服务器为每个 API“会话”分配某种针对每个客户端的锁或令牌。这就像试图用一根棍子对抗海洋的潮汐,因为你最终会失败和沮丧。

    您将如何识别客户?会话密钥? IP地址?他们使用的套接字的文件描述符(如果您使用 HTTP 之类的传输,可以在请求之间关闭连接......)?您为此选择的详细信息必须保留在服务器端,否则您将不得不在您的应用服务器上使用一些令人讨厌的旧粘性会话功能(如果是这样,如果他们使用的服务器出现故障,天堂会帮助您的客户中场)。

    您将如何处理不正常消失的 API 客户端?你会通过让收割线程清理空闲的会话锁来自动超时他们的会话锁吗?那就是更多的代码、更多的复杂性和更多隐藏错误的地方。如果 API 客户端从长时间的空闲时间回来并尝试重新使用过期的锁,应该如何构建客户端应用程序来处理这种情况?

我可以继续说下去,但希望你能明白我的意思。选择选项 1,然后变成无状态。否则,您最终将尝试在服务器端跟踪客户端状态。 唯一应该跟踪客户端状态的就是客户端本身。

【讨论】:

  • 我不认为在浏览结果集页面时接受内容更改是唯一的思维方式。您可能会看到重复条目——好的(无论如何客户都可以处理),但您也可能完全因为过去/以前页面范围内的删除而错过条目——不好。对于 Google 搜索结果可能无关紧要,但在其他情况下可能。
  • 作为替代方案,可以在客户端获取“所有 ID”(好的,对于大多数情况下不可行的 google 搜索结果)并逐页浏览此列表,在到达时加载更多内容具有该 ID 子集的结果集页面。坏消息是,问题又来了:可能同时删除了一些项目(无法再加载 - 必须以某种方式捕获),同时可能添加了您看不到的新项目浏览您当前的结果集。因此问题是:什么对你来说更重要?
  • 如果您在来回逐页浏览结果集时不关心丢失或重复的项目,那么您可以将页码或索引范围作为参数放入 URL - - 加载和丢弃之前放置在任何页面上的所有项目,从而加重服务负担。 (Google 是如何处理这个问题的?有人尝试过一个荒谬的查询并最初指定一个高页码,例如“query=millionaire&page=59786”?)
  • 另一方面,如果您希望在页面浏览期间获得稳定的结果集,同时提供新鲜的详细数据,您应该至少缓存查询结果的 ID 集(如果可行)并在浏览时遍历该集页。但是,您将不得不处理同时删除的项目,并且在到达“稍后询问查询时会出现”的页面之前,您不会看到添加的项目。 -- 看来,无论如何都要付出一些代价。
  • 顺便说一下,这种方法应该相当“RESTful”,因为在服务器端没有管理状态。如果您认为项目 ID 等同于指向任何单个项目的资源位置的超链接,那么它也是超媒体风格的(如果 ID 集实际上是 ID 资源定位器集,那么它将是完美的,但是更多的带宽和需要客户端内存)。
【解决方案2】:

可以保持资源状态。 “无状态禁止”仅指会话状态。

这是Roy Fielding's seminal REST derivation的摘录:

接下来我们为客户端-服务器交互添加一个约束: 通信必须是无国籍的,如在 第 3.4.3 节的 client-stateless-server (CSS) 样式(图 5-3), 这样从客户端到服务器的每个请求都必须包含所有 理解请求所必需的信息,并且不能采取 服务器上存储的任何上下文的优势。会话状态是 因此完全保留在客户端。

【讨论】:

    【解决方案3】:

    遇到这个问题,寻找处理并发的最佳实践, 并且从答案中,“Optimistic Concurrency”的任何参考答案都丢失了。

    【讨论】:

      猜你喜欢
      • 2016-03-11
      • 1970-01-01
      • 1970-01-01
      • 2012-10-09
      • 1970-01-01
      • 1970-01-01
      • 2020-05-18
      • 1970-01-01
      • 2018-09-30
      相关资源
      最近更新 更多