如果REST应用程序应该是无状态的,那么如何管理会话?

时间:2010-06-23 20:30:57

标签: rest session-state

我需要一些澄清。我一直在阅读有关REST和构建RESTful应用程序的内容。根据维基百科,REST本身被定义为 Representational State Transfer 。因此,我不理解所有这些无国籍的 gobbledeygook 每个人都在喷涌。

来自维基百科:

  

在任何特定时间,客户都可以在两者之间进行转换   申请状态或“休息”。处于休息状态的客户端能够   与其用户交互,但不会产生任何负载,也不会消耗每个客户端   存储在服务器集或网络上。

他们只是说不使用会话/应用程序级数据存储???

我认为REST的一个目标是使URI访问一致且可用,例如,而不是在帖子中隐藏分页请求,使请求的页码成为GET URI的一部分。我感觉合理。但似乎只是过分说明没有每个客户端数据(会话数据)应该存储在服务器端。

如果我有一个消息队列,并且我的用户想要阅读这些消息,但是当他阅读这些消息时,想要阻止某些发送者在其会话期间发出的消息,该怎么办?将它存储在服务器端的某个位置并让服务器只发送未被用户阻止的消息(或消息ID)是不是有意义?

每次请求新邮件列表时,是否真的必须发送整个邮件发件人列表?与我相关的消息列表首先不会/不应该是公开可用的资源。

再次,只是想了解这一点。有人澄清。


更新

我发现了一个堆栈溢出问题,其答案并没有让我一路走到那里: How to manage state in REST 这说明重要的客户端状态应该全部转移到每个请求上.... Ugg ..似乎有很多开销......这是对的吗?

16 个答案:

答案 0 :(得分:469)

基本解释是:

  

服务器上没有客户端会话状态。

无状态意味着服务器不会在服务器端存储有关客户端会话的任何状态。

客户端会话存储在客户端上。服务器是无状态的意味着每个服务器可以随时为任何客户端提供服务,没有会话关联粘性会话。相关会话信息存储在客户端上,并根据需要传递给服务器。

这并不排除Web服务器与之交谈的其他服务维护有关业务对象(如购物车)的状态,而不是客户端当前的应用程序/会话状态。

客户端的应用程序状态不应存储在服务器上,而应从客户端传递到需要它的每个地方。

REST 中的 ST 来自状态转移。您转移状态而不是让服务器存储它。 这是扩展到数百万并发用户的唯一方法。如果没有其他原因,因为数百万会话是数百万个会话。

会话管理的负载在所有客户端上摊销,客户端存储其会话状态,服务器可以无状态方式为多个数量级或更多客户端提供服务。

即使对于您认为只有 需要的服务,在数以千计的并发用户中,您仍然应该使您的服务无状态。成千上万仍然是成千上万,并且会有相关的时间和空间成本。

无状态是HTTP协议和Web通常如何设计来运行的,并且是一个整体更简单的实现,并且您有一个代码路径而不是一堆服务器端逻辑来维护一堆会话状态。

有一些非常基本的实施原则:

这些原则不是实施,您如何满足这些原则可能会有所不同。

总之,five key principles是:

  1. 给每个“东西”一个ID
  2. 将事物联系在一起
  3. 使用标准方法
  4. 具有多种表示形式的资源
  5. 无国籍地沟通
  6. REST dissertation中没有任何关于身份验证或授权的信息。

    因为从没有RESTful的请求验证RESTful请求没有什么不同。身份验证与RESTful讨论无关。

    解释如何为您的特定需求创建无状态应用程序,对于StackOverflow来说太宽泛

    实现与REST相关的身份验证和授权更加过于宽泛,并且在互联网上一般会详细解释各种实现方法。

      

    请求帮助/信息的评论将/应该被标记为   不再需要

答案 1 :(得分:270)

无状态意味着每个HTTP请求都完全隔离。当客户端发出HTTP请求时,它包含服务器完成该请求所需的所有信息。服务器永远不会依赖先前请求的信息。如果该信息很重要,则客户端必须在后续请求中再次发送该信息。无国籍状态也带来了新的特征。在负载平衡的服务器上分发无状态应用程序更容易。无状态应用程序也很容易缓存。

实际上有两种状态。生活在客户端上的应用程序状态和服务器上的资源状态。

当您实际提出请求时,Web服务只需要关心您的应用程序状态。其余的时间,它甚至不知道你存在。这意味着无论何时客户端发出请求,它都必须包含服务器处理它所需的所有应用程序状态。

每个客户端的资源状态都相同,其正确的位置在服务器上。将图片上传到服务器时,您将创建一个新资源:新图片具有自己的URI,可以成为未来请求的目标。您可以通过HTTP获取,修改和删除此资源。

希望这有助于区分无国籍状态和各种状态的含义。

答案 2 :(得分:74)

  

他们只是说不使用会话/应用程序级数据存储???

没有。他们并没有以微不足道的方式说出来。

他们说不要定义“会话”。不要登录。不要退出。为请求提供凭据。每个请求都是独立的。

您仍然拥有数据存储。您仍然拥有身份验证和授权。您只是不浪费时间建立会话和维护会话状态。

关键是每个请求(a)完全独立,(b)可以简单地在没有任何实际工作的情况下耕种到一个巨大的并行服务器场。 Apache或Squid可以盲目地成功传递RESTful请求。

  

如果我有一个消息队列,并且我的用户想要阅读这些消息,但是当他阅读这些消息时,想要阻止某些发件人在其会话期间收到的消息会怎么样?

如果用户想要过滤器,则只需在每个请求上提供过滤器。

  

让服务器只发送未被用户阻止的消息(或消息ID)是否有意义?

是。在RESTful URI请求中提供过滤器。

  

每次请求新邮件列表时,是否真的必须发送整个邮件发件人列表?

是。这个“要阻止的邮件发件人列表”有多大? PK的简短列表?

GET请求可能非常大。如有必要,您可以尝试POST请求,即使它听起来像是一种查询。

答案 3 :(得分:34)

你是绝对正确的,支持与服务器完全无状态的交互确实给客户带来了额外的负担。但是,如果考虑扩展应用程序,则客户端的计算能力与客户端数量成正比。因此,扩展到大量客户端更加可行。

只要您在服务器上承担一点责任来管理与特定客户端交互相关的一些信息,这种负担就会迅速增加以消耗服务器。

这是一种权衡。

答案 4 :(得分:32)

用户应用程序状态管理的历史视图

传统意义上的会话将用户的状态保持在服务器内的应用程序中。这可能是流中的当前页面或先前已输入但尚未持久保存到主数据库的页面。

这种需求的原因是客户端缺乏有效维护状态的标准,而没有特定于客户端(即特定于浏览器)的应用程序或插件。

HTML5和XML标头请求随着时间的推移标准化了在客户端(即浏览器)端以标准方式存储复杂数据(包括应用程序状态)的概念,而无需在服务器之间来回传递。

REST服务的一般用法

当需要执行某个事务或需要检索数据时,通常会调用REST服务。

REST服务旨在由客户端应用程序调用,而不是直接由最终用户调用。

身份验证

对于对服务器的任何请求,部分请求应包含授权令牌。如何实现它是特定于应用程序的,但通常是BASICCERTIFICATE形式的身份验证。

REST服务不使用基于表单的身份验证。但是,如上所述,REST服务并不是由用户调用,而是由应用程序调用。应用程序需要管理获取身份验证令牌。就我而言,我使用带有JASPIC with OAuth 2.0 to connect to Google for authentication的cookie和简单的HTTP身份验证进行自动化测试。我也使用HTTP Header authentication via JASPIC进行本地测试(尽管可以在SiteMinder中执行相同的方法)

根据这些示例,身份验证在客户端进行管理(虽然SiteMinder或Google会在其末尾存储身份验证会话),但对于该状态无法做任何事情,但它不是REST服务的一部分应用

检索请求

REST中的检索请求是GET操作,其中请求特定资源并且可以缓存。不需要服务器会话,因为请求具有检索数据所需的一切:身份验证和URI。

交易脚本

如上所述,客户端应用程序本身也调用REST服务以及它在客户端管理的身份验证。

这对REST服务的意义[如果正确完成]是对REST服务器采取单个请求将包含单个用户操作所需的所有操作,该操作执行单个事务中所需的所有操作,{{3是模式被调用的。

这通常是通过POST请求完成的,但也可以使用PUT之类的其他请求。

许多人为设想的REST示例(我自己这样做了)试图遵循HTTP协议中定义的内容,经过我决定更务实并将其留给Transaction ScriptPOST方法甚至不必实现POST-REDIRECT-GET模式。

尽管如此,正如我上面提到的,客户端应用程序将是调用服务的应用程序,它只会在需要时(不是每次都)调用带有所有数据的POST请求。这可以防止对服务器的持续请求。

轮询

虽然REST也可以用于轮询,但我不会推荐它,除非你因浏览器兼容性而必须使用它。为此,我将使用我设计了GET and POST only的WebSockets。 CometD是旧版浏览器的另一种选择。

答案 5 :(得分:25)

REST非常抽象。它有助于拥有一些好的,简单的,真实的例子。

以所有主要社交媒体应用程序为例--Tumblr,Instagram,Facebook和Twitter。它们都有一个永久滚动的视图,您向下滚动的距离越远,您看到的内容越多,时间越早越远。但是,我们都经历过那些你失去滚动位置的那一刻,应用程序会将你重置回顶部。就像你退出应用程序一样,当你重新打开它时,你又回到了顶端。

之所以如此,是因为服务器没有存储您的会话状态。可悲的是,您的滚动位置只是存储在客户端的RAM中。

幸运的是,您不必在重新连接时重新登录,但这只是因为您的客户端也存储了登录证书尚未过期。删除并重新安装应用程序,您将不得不重新登录,因为服务器未将您的IP地址与您的会话相关联。

您在服务器上没有登录会话,因为他们遵守REST。

现在上面的示例根本不涉及Web浏览器,但在后端,应用程序通过HTTPS与其主机服务器进行通信。我的观点是REST不必涉及cookie和浏览器等。存在各种存储客户端会话状态的方法。

但是让我们谈谈网络浏览器一秒钟,因为这带来了REST的另一个主要优势,这里没有人在讨论。

如果服务器试图存储会话状态,那么它应该如何识别每个客户端?

它无法使用其IP地址,因为许多人可能在共享路由器上使用相同的地址。那怎么样呢?

由于多种原因,它无法使用MAC地址,因为您可以在不同的浏览器和应用程序上同时登录多个不同的Facebook帐户。一个浏览器可以很容易地伪装成另一个浏览器,并且MAC地址也很容易被欺骗。

如果服务器必须存储一些客户端状态以识别您的身份,则必须将其存储在RAM中,而不仅仅是处理请求所花费的时间,否则它必须缓存该数据。服务器具有有限的RAM和缓存,更不用说处理器速度。服务器端状态以指数方式添加到所有三个状态。此外,如果服务器要存储有关您的会话的任何状态,那么它必须为您当前登录的每个浏览器和应用程序单独存储它,以及您使用的每个不同设备。

所以...我希望你现在看到为什么REST对可扩展性如此重要。 我希望你能开始明白为什么服务器端会话状态对服务器的可扩展性是什么焊接的铁砧对汽车加速。

人们感到困惑的地方是认为“状态”是指存储在数据库中的信息。不,它指的是在您使用服务器时需要在服务器的RAM中的任何信息。

答案 6 :(得分:12)

我发现这里的基本问题是将会话混为一谈。虽然REST指定您不应将 State 存储在服务器上,但没有任何东西阻止您存储用户 Session

管理服务器上的状态意味着您的服务器确切知道客户端正在做什么(他们在应用程序的哪个部分查看了哪个页面)。这就是你不应该做的事情。

我同意其他人说你应该把会话存储保持在最小尺寸;虽然这是常识,但它实际上也依赖于应用程序。 因此,简而言之,您仍然可以使用缓存数据保持会话,以便在服务器上以较少的负载处理请求,并通过为客户端提供临时身份验证/访问令牌来管理身份验证。每当会话/令牌过期时,生成一个新会话/令牌并要求客户端使用它。

有人可能会说客户端应该更好地生成令牌。我说它有两种方式,它取决于应用程序,以及谁将使用API​​。

同时在服务器上保留一些敏感会话数据应该是正确的方法。您不能相信客户端保留其购物车(例如)包含名为“isFreeGift”的字段。此类信息应保存在服务器上。

Santanu Dey 在他的回答中提供的视频链接很有帮助。如果你没有,请注意它。

只是旁注:似乎已经给出的所有答案似乎都忽略了某些操作可能会导致服务器负载过重的事实。这与功耗,硬件消耗和成本(由CPU周期租用的服务器)相关。一个优秀的开发人员不应该懒得优化他们的应用程序,即使在一些租用的服务器上的现代CPU上可以非常快速地完成操作,但是他们不支付电费和维护费用。

Althoght这个问题已经有几年了,我希望我的答案仍然有用。

答案 7 :(得分:11)

  

无状态意味着服务状态在后续请求和响应之间不会持续存在。每个请求都带有自己的用户凭据,并且单独进行身份验证。但在有状态中,每个请求都可以从任何先前的请求中获知。所有有状态请求都是面向会话的,即每个请求都需要知道并保留先前请求中所做的更改。

     

银行应用程序是有状态应用程序的一个示例。用户首次登录然后进行交易并注销。如果退出后用户将尝试进行交易,他将无法这样做。

     

是的,http协议本质上是一个无状态协议,但为了使其成为有状态,我们使用HTTP cookie。所以,默认情况下是SOAP。但它同样可以是有状态的,取决于你正在使用的框架。

     

HTTP是无状态的,但我们仍然可以使用不同的会话跟踪机制在我们的java应用程序中维护会话。

     

是的,我们还可以在webservice中维护会话,无论是REST还是SOAP。它可以通过使用任何第三方库来实现,也可以由我们自己实现。

取自http://gopaldas.org/webservices/soap/webservice-is-stateful-or-stateless-rest-soap

答案 8 :(得分:5)

看看这个演示文稿。

http://youtu.be/MRxTP-rQ-S8

根据这种模式 - 创建瞬态休息资源,以便在真正需要时管理状态。避免明确的会话。

答案 9 :(得分:3)

无状态与有状态之间的主要区别是每次都将数据传递回服务器。在无状态的情况下,客户端必须提供所有信息,因此可能需要在每个请求中传递大量参数。在Stateful中,cliet传递这些参数一次,它们由服务器维护,直到客户端再次修改。

IMO,API应该是无状态的,这样可以让我们快速扩展。

答案 10 :(得分:2)

您必须在客户端管理客户端会话。这意味着您必须为每个请求发送身份验证数据,并且您可能(但不一定)在服务器上有内存缓存,这会将身份验证数据与身份,权限等用户信息配对...

此REST statelessness constraint非常重要。如果不应用此约束,您的服务器端应用程序将不会scale,因为维护每个客户端会话将是Achilles' heel

答案 11 :(得分:0)

整个概念不同......如果您尝试实施RESTFul协议,则不需要管理会话。在这种情况下,最好在每个请求上执行身份验证过程(而在性能方面会有额外的成本 - 散列密码将是一个很好的例子。不是很重要......)。如果您使用会话 - 如何在多个服务器之间分配负载?我敢打赌RESTFul协议旨在消除任何会话 - 你真的不需要它们......这就是为什么它被称为无状态"。只有在完成请求后才能在客户端存储除Cookie以外的任何内容时才需要会话(以旧的,非Javascript / HTML5支持的浏览器为例)。如果是"全功能" RESTFul客户端通常可以安全地将base64(login:password)存储在客户端(在内存中),直到仍然加载应用程序 - 该应用程序用于访问唯一的主机,并且cookie不会被第三方脚本破坏。 ..

我强烈建议禁用RESTFul服务的cookie身份验证...查看基本/摘要身份验证 - 这对于基于RESTFul的服务应该足够了。

答案 12 :(得分:0)

REST是无状态的,在请求之间不保持任何状态。设置客户端Cookie /标头以维护用户状态(例如身份验证)。假设客户的用户名/密码已通过第三方身份验证机制进行了验证-第二级OTP验证等。一旦用户通过身份验证-标头/ cookie停止了服务端点的暴露,由于用户附带有效的标头/ cookie,我们可以假定用户为auth 。现在,某些用户信息(如IP)要么保留在高速缓存中,然后在此之后,如果请求来自列出资源的同一Ip(mac地址),则允许用户。缓存会保留特定的时间,一旦经过一段时间就会失效。因此,可以使用缓存,也可以使用数据库条目来保留信息的黑白信息。

答案 13 :(得分:0)

没有汤匙。不要认为无状态就像“一次又一次地将所有东西发送到服务器”。没门。总会有状态-数据库本身毕竟是一种状态,您是注册用户,所以没有服务器端,任何一组客户端信息都将无效。从技术上讲,您永远不会真正地无国籍。

但是您可以做的事和真正有意义的事,是将您的Web服务器占用的资源减至最少。像PHP这样的语言可以很轻松地将所有内容填充到会话存储中。您可以执行此操作,但是如果您有许多Web服务器,则它们必须使用它们之间共享的某些内容(NFS,Redis,Memcached等),以便您的下一个请求知道您之前单击过的内容。这是因为负载平衡-您可能会在下一个请求时最终到达另一个Web服务器。而且,尽管它们之间必须有一个共享存储(主要是因为它们必须告知您是否已登录),但是不应将其用作备用数据库。不是为了那个。

您是说将会话存储空间降到最低吗?

再次,这是您的决定。出于性能原因,您可以在此处存储内容(数据库几乎总是比Redis慢),可以冗余地存储信息,实现自己的缓存,无论如何-请记住,如果您存储大量垃圾,Web服务器将具有更大的负载在他们。而且,如果它们在重负荷下破裂(并且会破裂),您将丢失有价值的信息;用REST的思维方式,在这种情况下发生的所有事情就是客户端再次发送相同的(!)请求,并且这次得到了响应。

那该怎么办?

这里没有一个万能的解决方案。我会说选择无国籍水平,然后再去做。会议可能会受到某些人的喜爱而受到其他人的讨厌,但它们没有任何进展。对于每个请求,发送尽可能多的信息,也许更多;但不要将无状态视为没有会话,也不能解释为每次都在登录。 服务器必须以某种方式知道是你; PHP会话ID是一种好方法,手动生成的令牌是另一种方法。

思考和决定,不要让设计趋势为您考虑。

答案 14 :(得分:0)

这里的无状态意味着在服务器端不维护请求的状态或元数据。通过在服务器上维护每个请求或用户的状态,将导致性能瓶颈。仅要求服务器提供必需的属性以执行任何特定的操作。

要管理会话或为用户提供自定义体验,它需要维护一些元数据或用户可能的用户偏好,过去的请求历史记录的状态。可以通过维护cookie,隐藏属性或将其保存到会话对象中来实现。

这可以维护或跟踪应用程序中用户的状态。

希望这会有所帮助!

答案 15 :(得分:0)

开发RESTful服务时,要登录,您将需要对用户进行身份验证。一个可能的选择是每次您打算执行用户操作时发送用户名和密码。在这种情况下,服务器将根本不会存储会话数据。

另一种选择是在服务器上生成会话ID并将其发送给客户端,因此客户端将能够将会话ID发送至服务器并进行身份验证。这比每次发送用户名和密码要安全得多,因为如果有人得到了他们的帮助,那么他/她就可以模拟用户,直到用户名和密码被更改为止。您可能会说,即使会话ID也可能被盗,在这种情况下,用户将被假冒,您是对的。但是,在这种情况下,只有在会话ID有效的情况下才可以冒充用户。

如果RESTful API需要用户名和密码来更改用户名和密码,那么即使有人使用会话ID冒充了用户,黑客也将无法锁定真实用户。

可以通过单向锁定(加密)某种方式(识别用户的身份)并将时间添加到会话ID中来生成会话ID,这样可以定义会话的到期时间。

服务器可能会或可能不会存储会话ID。当然,如果服务器存储了会话ID,则它将违反问题中定义的条件。但是,仅重要的是确保可以为给定的用户验证会话ID,而不必存储会话ID。想象一下,您对电子邮件,用户ID和一些特定于用户的私人数据(如喜欢的颜色)进行单向加密,这将是第一级,并以某种方式将用户名日期添​​加到加密的字符串中,然后应用两个方式加密。结果,当接收到会话ID时,可以解密第二级,以便能够确定用户声称是哪个用户名以及会话时间是否正确。如果这是有效的,则可以通过再次进行加密并检查它是否与字符串匹配来验证第一级加密。您无需为此存储会话数据。