如何使应用程序保持无状态


97

这可能是一个令人费解的问题,但是我试图更好地理解无状态。

根据我的阅读,Web应用程序应该是无状态的,这意味着每个请求都被视为独立的事务。因此,应避免使用Session和Cookies(因为它们都是有状态的)。更好的方法是使用令牌,令牌是无状态的,因为服务器上没有存储任何内容。

因此,我试图理解,当会话中保留有数据(例如购物车中的物品)时,Web应用程序如何变得无状态?这些实际上存储在某个地方的数据库中,然后定期清除吗?当您使用令牌而不是cookie时,这如何工作?

然后作为一个相关问题,主要网站(亚马逊,谷歌,Facebook,Twitter等)是否实际上是无状态的?他们使用令牌还是cookie(或两者都使用)?


38
我最近看过并与沉迷于无国籍状态的开发者交谈,这使他们分心。对可重用性具有无状态性是很好的,但是在每种情况下都比其他目标都追求该目标是不现实的,除非您出于某种原因(例如扩展)而拥有大量资源来做到这一点。
马克·罗杰斯

4
@MarkRogers为什么?无国籍与可复用性无关。无状态并不会导致更高的努力。
Paul Wasilewski

3
@PaulWasilewski:无国籍并不会带来更大的努力。=>它确实可以,通过有状态应用程序,您可以将所有内容保留在与会话相关的内存中。尽管可以与会话固定一起使用,但这不能很好地扩展,但是非常简单。当服务器需要开始彼此之间交换信息时,麻烦就开始了。
Matthieu M.

6
查看亚马逊,您会发现即使更换计算机,您的购物车仍然存在,因此它不会存储在Cookie中,而是存储在数据库中。
njzk2

20
如果您没有阅读我的答案。这是简短的版本:Web请求本质上是无状态的。Web 应用程序不是。(无论什么“无状态网络”教条主义者告诉你!)
svidgen

Answers:


95

“ Web应用程序应该是无状态的”应该理解为“除非有充分的理由要具有状态,否则Web应用程序应该是无状态的”。“购物车”在设计上是有状态的功能,并且否认这会适得其反。购物车模式的重点是保留请求之间的应用程序状态。

这是我的另一种可能想象为一个无状态的网站,实现了购物车将是一个单页的应用程序,其保持的购物车完全客户端片面,检索与AJAX调用的产品信息,然后将其全部一次,当发送到服务器用户进行结帐。但是我怀疑我是否曾经见过有人这样做,因为它不允许用户使用多个浏览器选项卡,并且在用户意外关闭选项卡时也无法保留状态。当然,有一些解决方法,例如使用localstorage,但是您确实再次拥有了状态,只是在客户端而不是在服务器上。

每当您有一个Web应用程序需要在两次浏览之间保留数据时,通常都通过引入会话来实现。可以通过Cookie或添加到每个链接的URL参数来标识请求所属的会话。应该首选Cookie,因为它们可以使您的URL更方便,并防止您的用户意外共享其中包含其会话ID的URL。但是,使用URL令牌作为后备对停用Cookie的用户也至关重要。大多数Web开发框架都有一个会话处理系统,它可以立即执行此操作。

在服务器端,会话信息通常存储在数据库中。服务器端内存中缓存是可选的。它可以大大缩短响应时间,但不允许您在不同服务器之间传输会话。因此,您将需要一个持久数据库作为后备。

主要网站(亚马逊,谷歌,Facebook,Twitter等)实际上是无状态的吗?他们使用令牌还是cookie(或两者都使用)?

他们允许您登录吗?当您关闭选项卡并重新访问该站点时,您仍在登录吗?如果您是,则他们将使用Cookie来保留会话之间的身份。


46
我认为这里的困惑之一是广义上的“ Web应用程序”和“ Web服务器上运行的代码”之间的区别。通常认为后者是无状态的,而不是前者。正如您所说,前者通常是无状态的,因为状态通常是业务逻辑的一部分。对于后者来说,无状态只是意味着状态需要存储在客户端或数据库服务器或这两者上,而不是存储在Web服务器上。
德里克·埃尔金斯

16
“ [...]但是您确实又有状态,只是在客户端而不是在服务器上。” 这是关于在服务器端没有状态,以实现更好的可伸缩性和可用性。状态是否存储在客户端无关紧要。
Paul Wasilewski

5
@ njzk2您可以详细说明一下,以免听起来很荒谬吗?用户不会去亚马逊购买更多名称。而且,在他们购买商品之后,一些东西消失了,这些东西仅在他们购物时才存在。如果那不是“应用程序的状态”,那是什么?如果应用程序没有状态,那么它们有什么?

3
@nocomprende:我认为njzk2的总要旨是您购物车中的内容(如您的全名)是Web应用程序在服务器端持续存在的数据。当人们说“ webapps应该是无状态的”时,它们通常表示不同于“ webapps不应访问包含与用户名相关联的全名的数据库”的含义。确切地讲,它们 “无状态”的含义可能没有被简单定义,因为一旦有了该数据库,便可以将各种废话保留在其中,以支持过于复杂的应用程序状态,但不应该;-)
史蒂夫Jessop

4
@nocomprende:通过回滚数据库来打乱鸡蛋:由于我们的web应用程序是无状态的,因此可以像以前一样恢复;-)
Steve Jessop

56

Web应用程序确实应该是无状态的。但是,会话变量,Cookie和令牌在它们都存储在客户端(Web浏览器)上时,不会违反此要求。它们可以是请求中的参数。

这是一个简化的模型:

Web Browser (has state) <-> Web Server (stateless) <-> Database (has state)

这可能适用于软件工程堆栈交换。我输入的答案是Web浏览器状态的一部分。只要这是唯一的位置,除了我之外,其他任何人都无法访问。但是,一旦我点击,Post your Answer浏览器就会将其发送到Web服务器。Web服务器不使用其自身的状态来处理帖子。它通过浏览器和数据库了解我的身份。检查完我的帖子并将其添加到数据库后,Web服务器会立即忘记我。

那就是无状态的意思。服务器不负责记住这些。那不是它的工作。

这样做有很多优点。如果Web服务器发生内存泄漏,则可以检测到,因为它的内存占用空间不会增加。如果Web服务器崩溃,我的身份将不匹配。如果有人试图进行拒绝服务攻击,则他们不能使用Web服务器状态资源来进行此操作,因为Web服务器不会在会话之间为他们分配任何状态。这都是为了使Web服务器稳定。这样,当它开始运行时,它将保持运行状态。

现在可以肯定的是,我正在消耗数据库中的资源,但是首先已经通过一些稳定的方法对这些资源进行了检查,我们可以依靠这些稳定的方法来保护数据库免受狂乱的网络攻击:无状态,强制执行业务规则的Web服务器。


8
我不知道。。。这个答案听起来有点像说:“ Excel不会存储您的电子表格,磁盘驱动器会存储!” 哈哈,就大多数人而言,数据库不是Web服务器的一部分吗?显然,状态未存储在CPU或服务器的代码中,并且将其全部保留在内存中是非常愚蠢的。

7
@nocomprende不,数据库通常不属于Web服务器。是的,将状态存储在数据库中很可能会限制整个应用程序的可伸缩性,但是相对而言,很少有应用程序可以将其所有状态(甚至所有“每用户”状态)卸载到客户端上。数据库服务器是专门为可伸缩处理状态而设计的,并且正如CandiedOrange提到的那样,它们通常比Web服务器受到更好的保护,配置和审查。即使不能解决所有可伸缩性问题,轻松扩展Web服务器也有好处。
德里克·埃尔金斯

9
@nocomprende:说数据库不是Web服务器的一部分是因为您可以为1个,2个,3个... Web服务器拥有一个数据库(或数据库集群)。这就是无状态意味着增加可伸缩性的方式:您可以独立扩展数据库集群和Web服务器的数量。
Matthieu M.

6
“ Web应用程序确实应该是无状态的。” 不,这是完全废话。
svidgen

4
这个答案是我最喜欢的答案,因为它最能说明Web开发人员中“无状态”的用法。服务器在请求之间不保持状态。所有状态都必须来自客户端(即请求的一部分)或数据库(可能基于请求)。顺便说一句,Web应用程序中通常会存在某种状态(例如,事物的静态实例),但是总的来说,您希望将事物设计为无状态。这个答案似乎比公认的答案更好,因为它实际上最好地解释了无状态的想法。

30

Web应用程序应该是无状态的

废话。Web 请求应该是无状态的。或更准确地说,Web请求无状态的。

但是,说整个应用程序应该是无状态的完全是胡说八道。

每个请求都被视为独立交易。

是的,完全是或者更准确的说,是的,一定通过HTTP,每个请求本质上独立于所有其他请求。向HTTP添加“状态”要求您为每个“状态”请求显式标识,存储和检索“状态”。这会费力,降低性能并增加复杂性。

而且,由于这些原因,每个可能是无状态的请求应该是无状态的。

因此,应避免使用Session和Cookies(因为它们都是有状态的)。更好的方法是使用令牌

一些事项:令牌也可以绑定到会话存储。Cookies不会需要被束缚到会话存储。令牌通常存储在cookie中。而且,有时会话只是工作的正确工具。

这意味着,至少有时,会话和cookie与令牌一样“更好”!

[令牌]是无状态的,因为服务器上没有存储任何内容。

好吧,就是这样。就是“无国籍”教条的真正含义。虽然,很明显,这不是要在服务器上存储“空”,而是要在服务器上不存储会话状态。

例如,我的Gmail收件箱处于状态。而且最好将其存储在服务器上!但是,这不是会话状态。

因此,与其让服务器使用一个很小的标识符来弄清楚自己是谁,等等,无状态应用程序还想提醒您自己是谁,以及每个血腥的请求在做什么应用程序状态仍然存在,客户端只负责保持它。

现在,如果该状态很小,那可能就可以了。在某些情况下,它非常好。

然后,当然,有些事情我们只是希望成为有状态的...

当有会话要保留的数据(例如购物车中的物品)时,Web应用程序如何变为无状态?这些实际上存储在某个地方的数据库中,然后定期清除吗?

两种选择。您正在开会或被拒绝!

... 不过实话说。您通常不会将购物车存储在Cookie中。诸如购物车之类的东西将被存储在“传统”会话中,或者将被存储为Cart对象,并带有服务器用来将其拉入后续请求的某种ID。有点像..呃...错误... 会话。

真的很认真:在很大程度上,当两个通信代理可以将对话中的消息上下文化时,“状态”就是我们所说的。传统上理解的会话就是我们通常所说的发生机制。

我认为,无论您对服务器处理的每个请求使用令牌还是“会话”,您都需要将该请求上下文化以实现它,或者不这样做。如果不需要上下文,请不要获取它。如果上下文必要的,则最好将其放在附近!

然后作为一个相关问题,主要网站(亚马逊,谷歌,Facebook,Twitter等)是否实际上是无状态的?他们使用令牌还是cookie(或两者都使用)?

可能两者都有。但是,总的来说,它们确实可以完成您的工作:他们设置cookie来识别大型“会话”数据库中的“状态”记录。

我怀疑,如果可能的话,他们会将基本身份声明推入短暂的“令牌”中,以避免在集中式存储上发生不必要的争执。但是,许多服务允许我“注销所有其他位置”这一事实可以很好地表明,如果它们完全使用令牌,那么它们至少会得到半传统会话模型的“支持”。 。


3
同意。它使我想起“不变数据”的想法。如果它是一成不变的,请将其刻在一块岩石上,不要浪费计算机来做到这一点。让计算机处理数据!这就是我们建造它们的原因!应用程序处理数据。不变的数据是无用的。

@nocomprende仅供参考,稍后我将对此做附录。我觉得我的答案缺少了OP基本问题的一部分。因为,有一个合法的关注浮动背后的“无状态应用”的想法。但是,答案是,当人们说“无状态”时,他们的意思是“最少地依赖服务器端会话”。
svidgen '17

4
@nocomprende:不变的数据结构有些不同,它是用于管理内存对象生命周期的工具。
whatsisname

1
喜欢您的第一条解释。当我们讨论某件事时,我们做出的每一个口头陈述都会立即消失。但是以某种方式,我们仍然可以进行对话,是吗?这是魔术!

1
@nocomprende这是一个有趣的讨论,但是我想我们不应该在这里继续。
pabrams

14

有状态性不一定是一件坏事,但是您需要了解有状态和无状态应用程序之间的区别。简而言之,有状态应用程序会维护有关当前会话的信息,而无状态应用程序则不会。永久存储为用户帐户一部分的信息可能会也可能不会存储在会话中,但是存储与用户帐户有关的信息本身不会使应用程序处于有状态状态。有状态性要求服务器维护有关当前用户会话的信息,而不是客户端浏览器维护的信息。例如,客户端可以进行身份​​验证并获得JSESSIONID cookie,然后将其与每个请求一起发送到服务器。如果服务器根据此JSESSIONID开始在应用程序的会话范围中存储内容,则它将变为有状态。

无国籍

“无状态”是指服务器和客户端未维护有关用户会话的当前信息。客户端和服务器可以使用某种形式的令牌来提供请求之间的身份验证,但不会存储其他任何当前信息。这种解决方案的典型用例可能是新闻站点,其中大多数用户(新消费者)消费信息,但不产生返回该站点的信息。在这种情况下,站点不需要维护有关当前用户会话的任何信息。请注意,该站点仍可以使用cookie来识别用户并存储有关该用户对站点使用情况的信息,但是由于记录的所有内容都可能是事务性的,例如,用户单击的链接可能被记录,因此该站点仍可以被视为无状态。服务器,但未在用户会话中维护。

服务器上的状态

在服务器上,有状态应用程序保存有关当前用户的状态信息。这种方法通常涉及使用cookie来标识用户的系统,以便可以在两次请求之间在服务器上维护状态。会话可能会或可能不会通过身份验证,具体取决于应用程序的上下文。有状态服务器应用程序具有在服务器上缓存用户状态信息,加快查找和页面响应时间的优势。不利的一面是,在会话范围内存储信息非常昂贵,并且大规模地它会占用大量资源。它还为黑客创建了潜在的攻击媒介,以试图劫持会话标识符并窃取用户会话。有状态服务器应用程序还面临着保护用户会话免受意外服务中断(例如服务器故障)的挑战。

对客户的状态

通过使用JavaScript和sessionStorage等现代浏览器技术,应用程序现在可以轻松地在该用户设备上存储有关该用户会话的状态信息。总体而言,该应用程序仍可以被认为是有状态的,但是维护状态的工作已经转移到了客户端。这种方法(对于Web应用程序的维护者)相对于维护服务器上的状态具有很大的优势,因为每个用户实际上都在维护自己的状态,并且对服务器基础结构没有负担。在网络规模上,这种架构选择会对硬件和电力成本产生巨大影响。维护服务器状态每年可能要花费数百万美元。转移到客户端维护状态的系统,每年可以节省数百万美元。

代币与Cookie

Cookies充当客户端设备/浏览器的标识符。它们可以用来存储所有事物,但是通常它们存储某种形式的标识符,例如CFML应用程序中的CFID / CFTOKEN。可以将Cookie设置为长时间存在于用户的浏览器中,从而可以在浏览器会话之间进行诸如维护应用程序身份验证之类的事情。Cookie也可以设置为“仅内存”,这样,当用户关闭浏览器时,它们就会过期。

令牌通常是关于用户的某种识别信息,这些信息是在服务器上生成的(使用加密对信息进行加密),传递给客户端,然后与后续请求一起返回给服务器。它们可以在请求和响应的标头中传递,这是单页应用程序中的常见模式。理想情况下,每个请求/响应都会生成一个新的令牌,因此攻击者以后无法拦截和使用令牌。

单页应用程序和客户端状态

使用SPA,状态信息被加载到客户端浏览器中并在其中进行维护。当状态发生变化时(例如,您将更新发布到社交媒体帐户中),客户端会将新交易中继到服务器。在这种情况下,服务器会将更新保存到数据库之类的持久数据存储中,并根据更新(例如,更新ID)将需要与服务器进行同步的任何信息转发回客户端。

请注意,这种在客户端上存储状态的模式为联机/脱机体验提供了优势,因为您可以从服务器断开连接,同时仍可以使用一些可用的应用程序。Twitter是这种情况的一个很好的例子,即使您与Twitter服务器应用程序断开连接,也可以在Twitter feed中查看客户端中加载的所有内容。这种模式还会使服务器和客户端之间的同步变得复杂,这是其自身的全部主题。解决方案的复杂性是在客户端上能够维持状态的权衡。

客户端上的有状态性使Web应用程序感觉和行为更像传统的桌面应用程序。与桌面应用程序不同,通常不会在浏览器中将所有帐户信息加载到客户端会话中。这样做在许多情况下是不切实际的,并且会产生不良的经验。您能想象尝试将整个Gmail框加载到浏览器中吗?相反,客户端维护的信息包括您正在查看的标签/文件夹以及您正在查看的文件夹中电子邮件列表中的位置。平衡要维护的状态信息和根据需要请求的状态信息是此模式的另一项工程挑战,并且再次代表了在实用性和提供良好用户体验之间的权衡。

购物车之类

至于购物车之类的细节,实际上取决于解决方案。购物车可以存储在服务器上的数据库中,也可以仅存储在服务器上的会话范围内,甚至可以存储在客户端中。亚马逊拥有针对登录用户的持久性购物车,以及针对匿名用户的“临时”购物车,尽管这些购物车在某种程度上是持久性的。

当您谈论类似Google之类的东西时,实际上是一堆使用同一品牌的不同应用程序,它们可能不共享相同的体系结构,并且每种构建的方式都可以最好地满足其用户的需求。如果要了解网站的构建方式,请在浏览器中打开开发人员工具并进行查看。检查cookie,观察网络流量,并查看其运行方式。

很抱歉,如果这个答案有点混乱,但是有状态性是一个复杂的话题。


6

不必避免会话和Cookie。您仍然可以将它们与无状态Web应用程序一起使用。

Java和Ruby on Rails之间有很大的区别。Java应用程序将使用存储在cookie中的会话密钥将会话存储在内存中。这样可以快速检索用户状态和购物车。但是,您必须始终在会话中使用同一台服务器。

Rails应用程序将用户ID存储在签名的加密cookie中。它不能被篡改。加载页面时,Web应用程序将从数据库中获取您的状态,用户和购物车。这比较慢,但是关键是,您可以击中任何实例!这使您可以随意重启,扩展,关闭实例。很方便。还可以通过共享的内存中缓存数据库(如Redis)使其更快。或者,您也可以将购物车存储在Cookie中,前提是它足够小。

因此,您可以通过巧妙的技术来实现无国籍状态,并随意扩展规模。



5

当提到无状态时-例如在RESTful HTTP服务中-它将避免在服务器端存储状态。充其量,这还包括避免将任何状态存储在数据库或后端的其他持久性存储中。为了清楚起见,我说的是一般而言不是数据的状态。似乎有些人正在混淆。

无状态通信具有多个好处,尤其是在可伸缩性和可用性方面。

更好的方法是使用令牌,令牌是无状态的,因为服务器上没有存储任何内容。

确实如此(对于某些身份验证和授权协议)。令牌可以(但不能本身)提供请求中身份验证用户或授权操作所需的所有信息。举个例子,看看JWT

因此,我试图理解,当会话中保留有数据(例如购物车中的物品)时,Web应用程序如何变得无状态?这些实际上存储在某个地方的数据库中,然后定期清除吗?当您使用令牌而不是cookie时,这如何工作?

关于购物车示例。在不使用会话或cookie的情况下,将所有购物车项目存储在客户端是没有问题的。您可以在smashingmagazine.com上找到示例。但是也有可能实现带有cookie的无状态购物车(至少如果您的分类不是那么大并且4kb的存储空间就足够了)。

不要误会我的意思,这并不意味着您应该实现无价无价的购物车。亚马逊或其他大型在线购物平台正在使用有状态购物车实施方式,因为用户体验和可用性对他们而言比满足可伸缩性等非功能性技术要求更为重要。

通常,令牌不用于存储购物车项目之类的信息。它们用于无状态验证和授权。

然后作为一个相关问题,主要网站(亚马逊,谷歌,Facebook,Twitter等)是否实际上是无状态的?他们使用令牌还是cookie(或两者都使用)?

如果您询问他们是否使用Cookie或令牌进行身份验证,那么答案是他们同时使用两者。对于大多数用户来说,技术客户通常使用cookie。


-2

好的,您引用的规则在技术上是不正确的。Web应用程序的所有层都具有状态。

该规则的目的是“不保留每个会话状态服务器端”。

ASP中的会话变量,通常用于执行购物篮/用户名中的项目等操作。

原因是随着应用程序获得更多用户,服务器上的内存将用完。将存储移动到数据库或共享缓存并不能解决问题,因为您仍然遇到瓶颈。

要保持每个用户的应用程序状态而不会遇到此问题,请将状态移到客户端。例如,将购物篮中的商品放入Cookie或更高级的客户端存储中。

由于客户端数量随用户数量扩展,因此您的应用程序整体上不会出现瓶颈,并且可以很好地扩展。


2
尽管内存泄漏和拒绝服务问题是一个因素,但我认为当今更重要的驱动因素是Web服务器故障的弹性和健壮性,这当然也与可伸缩性有关。这个想法是,如果服务器超负荷甚至崩溃,我可以将未来的请求(甚至更加小心,甚至重播失败的请求)重新路由到新的Web服务器,而无需协调或丢失状态(如用户所见)。
德里克·埃尔金斯

嗯。如果服务器上有每个用户的信息,即使它是分布式的,您仍然会遇到可伸缩性问题。
伊万(Ewan)

如果从磁盘提取数据是诸如缓存之类的瓶颈,那么您可以做很多事情。
JeffO

不,如果您保留每个会话的数据,则存在一个固有的问题。您可以将其从网络服务器移至其自身的高可用性系统,或者将其移至客户端以将其全部删除
Ewan,2017年

1
所有关于避免生土豆的讨论都使我迷惑不解。俗话说“降价就此止步”怎么了?需要某种数据来管理数据,我的银行不希望我将所有财务交易信息仅保留在笔记本电脑上。为什么每个人都逃避数据尖叫?这就是为什么我们有计算机!疯。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.