无状态 Web 应用程序,一个都市传说?

Stateless web application, an urban legend?

这几天在努力理解token-based authentication,号称是stateless authentication方法。我遇到了stateless web application的概念。

以下是我读到的一些话题:

起初,我对这个想法感到很兴奋。但是越来越觉得stateless是一个pseudo-proposition.

比如,假设我们使用client-storedtoken进行认证,我们如何统计在线用户(假设没有日志)?我们应该将令牌存储在数据库中吗?这是否意味着我们将状态信息存储在服务器上?更重要的是,数据库中的姓名、年龄等普通用户信息是否也是某种状态信息?

我认为这里真正的问题不是让网络应用程序无状态,而是让网络应用程序正确处理状态信息,这样它就不会危及可伸缩性

这取决于如何解释这个词stateless:

  1. Web 应用程序没有状态。
  2. 或者网络应用程序本身不存储状态

我更喜欢 2,因为总有一些 inevitable global state(引自@deceze 对他的回答的评论)。而且无论我们将状态信息存储为 HTML 5 网络存储,或 HTTP header,或隐藏表单字段,或 Cookie,状态仍然存在。只是它存储在服务器以外的其他地方。

我错过了什么好东西吗?任何人都可以阐明这一点,以便我可以从这种精神斗争中解脱出来吗?

添加 1

刚读了 Leonard Richardson 的书 RESTful Web Services。在第4章中,在Statelessness节的末尾,将状态分为Application StateResource State。所以我之前提到的普通用户信息和数据,比如图像等,可以归类为Resource State。而stateless指的是Application State。所以在服务器上存储 resource state 不会破坏无状态代码。

但是这本书也提到了an application key is used to restrict how many times a user can invoke a web service.它承认这样的信息不能存储在客户端的场景。并且必须将其存储在服务器端会破坏无状态代码并引入 session 亲和力问题。 它声称无状态可以避免 session 亲和性问题,但没有解释如何。 我真的不知道无状态如何处理这种情况。有人可以在这里阐明一下吗?

好的,我认为 无状态 Web 应用程序 这个术语没有任何意义。有意义的是无状态协议。无状态协议是一种独立处理每个请求的协议。

所以在你的情况下,如果你在每个请求中发送一个授权令牌,那么它就是无状态的。这就是 HTTP 身份验证应该如何工作的。

另一方面,如果您只发送一次身份验证令牌并且每个连续的请求都不必发送(例如因为服务器知道此 TCP 连接已经过身份验证)那么这意味着每个请求都取决于身份验证要求。这使协议有状态。

无状态协议更易于扩展、更易于代理等

现在,对于 Web 应用程序,该术语可能有意义也可能没有意义,具体取决于定义。虽然我不知道有什么合理的。

旁注: stateful/stateless 与客户端和服务器之间的数据共享无关。

我认为无状态身份验证和无状态应用程序并不像您想象的那样相关;无状态这个词在这里有两种不同的语境。

无状态身份验证是一种识别客户端身份的方法,无需携带来自先前客户端请求或交互的任何 information/state,这与 cookie 不同。

无状态网络应用程序?当然,它们是可能的,但这完全取决于是否必须保留用户数据,也就是说,它实际上取决于所讨论的应用程序。

"state"只是真正指的是客户端和服务器之间的状态。服务器当然会存储数据,从技术上讲,您可以在服务器上看到任何数据的任何修改,如"altering state"。因此,"stateless" 这种意义上的应用完全没有实际意义。

"stateless"指的是服务器是否在任何特定时间处于允许特定客户端向其发送特定请求的状态。

考虑:对于传统的 cookie-based 登录会话,服务器仅 处于接受客户端请求的状态 有限的时间 window ;只要当前会话有效。客户无法预测那是多长时间。在任何时候,来自客户端的请求都可能会失败,因为 服务器上的某些状态 超时。在这种情况下,客户端需要重新登录来重置服务器的状态。

将此与基于令牌的身份验证进行对比。令牌必须无限期有效。它本质上是用户名和密码的替代品。为了便于讨论,假设客户端在每次请求时都会发送他们的用户名和密码。这意味着每个请求都可以根据自己的优点进行身份验证,不需要服务器处于某个特定的时间 "state".

您使用令牌而不是用户名和密码的原因有两个:

  1. 您可以使用同一个帐户授权多个客户端,但每个客户端都有其单独管理的凭据
  2. 您不希望每个请求都来回发送 "master password"

当然,服务器需要跟踪创建的令牌并针对每个请求对某些数据库进行身份验证。这是一个无关紧要的实现细节。这与使用会话 cookie 没有区别;但是,由于令牌无限期有效,因此可以更轻松地缓存请求,而无需复制临时会话存储。

最后一个需要先发制人的论点:无限期会话和无限期令牌之间有什么区别,会话结束时与令牌何时可以撤销有什么区别?
会话结束时,可以使用其他 "master credentials"(重新登录)重新建立。令牌 can/should 只有在主动撤销时才会结束,这类似于撤销完全为主凭据访问服务的授权,而不是常规应用程序流程的一部分。


更一般地说:将无状态 HTTP 协议与 FTP 等有状态协议进行对比。在 FTP 中,服务器和客户端需要保持共享状态同步。例如,FTP 协议具有 CWD 命令 更改当前工作目录 。也就是说,在任何给定时间都有一个客户端 "is in" 目录的概念。后续命令的行为因所在目录而异。这是有状态的。您不能在不知道该状态的情况下随意发送命令,否则您将无法预测结果。


无状态 client/server 通信首先简化了客户端,因为客户端可以随时假设能够请求服务器的任何内容,而无需知道 状态 服务器("is my session still active or not?"、"what directory will this action affect?")。 它可以帮助扩展服务器实施,因为只需要在所有服务器之间复制静态信息,而不是不断变化的有效会话池及其相关数据。


在架构上,您的目标应该是拥有尽可能多的无状态组件。这将简化横向扩展。例如,如果您的 Web 服务器保留本地会话存储,那么很难将您的 Web 服务器扩展到负载后的多个实例 balancer/CDN。一个改进是将会话存储集中到一个独立的数据库中;现在您可以拥有多个 stateless Web 服务器,它们知道如何从某处获取数据(包括会话数据)并可以呈现模板,但在其他方面完全可以互换。

但是,会话存储 必须在所有试图访问它的人之间保持完美同步,这使得扩展变得困难. 令牌通过降低数据更改的频率(仅在添加或删除令牌时)来改进这一点,这意味着如果您希望在可能的多个位置拥有多个令牌存储,您可以使用分布式数据库或其他更简单的复制机制, and/or 使该数据可缓存。