有状态 (Stateful) 和无状态 (Stateless) 是网络服务设计的两种模式, 虽然很多场景下有状态和无状态的设计都是可行的, 但实际上二者之间有着根本性的区别.

状态 (State)

状态的定义是 "由一组物理量定义的, 一个系统所处的状况", 在接下来讨论的内容中, 也可以说, 状态是由一组数据表征的, 一个系统所处的状况.

当我们访问 Google Doodles 时, 每天看到的 Google doodles 都是不一样的, 因此显示的 doodles 就是这个页面的状态.

在有状态 / 无状态网络服务中, 状态都指的是请求的状态, 大多数情况下, 也就是用户的状态, 或者说用户的登录状态.

有状态 (Stateful)

上面提到, "有状态" 中的 "状态" 指的是用户的状态, 那么有状态性 (statefulness) 的定义就是: 资源的描述会随用户状态改变而改变的特性.

把网络服务想像成一家酒店, 有客户 A 入住, 前台登记了客户的信息后, 没有给客户钥匙, 而是把客户信息 (客户身份, 入住时间, 房间号等数据) 存进了酒店管理系统中. 这些信息就是客户的 "状态". 第二天客户出门回到酒店, 前台需要根据客户身份证号查询系统, 然后把客户带到他的房间. 这就是典型的有状态系统.

有状态网络服务

我们为什么要保存用户请求状态? 因为很多的服务, 需要对不同用户查询不同的数据, 并返回不同的响应. 比如 Twitter, Tumblr. 根据有状态性的定义, 可以把网络服务分为有状态的和无状态的两类.

准确来说, 有状态 API 指的是: 存在用户状态, 同时用户状态由服务器管理的 API.

典型的有状态服务使用会话 (session) 保存状态, 即在服务端保存一个用户会话, 用户发起请求时, 服务器从会话池中找到该用户, 然后返回针对该用户的响应.

有状态服务不一定使用会话保存状态, 但存在会话的服务肯定是有状态的.

有状态设计存在很大的缺陷, RESTful API 的流行, 是因为人们发现有状态设计只适用于少数应用, 更多的时候它存在很大的问题. 最大的一个问题就是, 你会发现服务器实际上需要保存每一个用户的状态.

以会话为例, 如果有 1,000,000 个用户正在使用你的网站, 那么服务器就需要保存 1,000,000 个用户的会话, 实际上服务器保存的会话数要远超 1,000,000, 因为还有很多过时的会话未及时关闭.

这个问题是有状态设计不可能解决的, 因为实际上用户对服务器是不可知的, 10 分钟前一个用户发送了一个请求, 而且已经连续 10 分钟没有发送新请求了, 那服务器是否应该关闭这个用户的会话? 谁也不知道. 也许用户一直在看同一个页面, 也可能用户的终端已经崩溃掉了...

虽然现在有很多手段 (如缓存) 可以改善这个问题, 但是很多情况下有状态的局限性是显著的.

从另一个角度看, 其实现在的大多数客户端有一定的存储和运算能力, 有状态设计这种把所有状态保存在服务器的做法, 相当于把这些客户端都视为哑终端看待, 实际上是一种性能的浪费.

无状态 (Stateless)

与有状态性相反, 无状态性 (statelessness) 指的是: 资源的描述不会随用户状态改变而改变的特性.

想像和上面同样的场景, 客户 A 入住一家酒店, 酒店确认客户的要求并成功收款后, 没有记录任何状态, 而是根据客户入住时间给客户发了一把 "只能在特定时间打开特定房间门" 的智能钥匙 (token). 第二天客户出门回到酒店, 不用查询任何系统, 就可以直接进入自己的房间. 这就是典型的无状态系统.

当然, 对整个系统来说, 钥匙是否可用还是需要校验的, 但钥匙与用户是相互独立的对象, 这把钥匙可用不可用不是客户的状态. 最为关键的一点, 钥匙的验证不需要查询数据库, 这样的自我校验特性被称为内含状态 (self-containd state).

无状态网络服务

无状态设计通过客户端自校验解决了有状态设计的最大问题, 即, 服务器不再保存用户状态, 用户可以发送密码 (也可能是私钥) 换取代币 (token), 然后把代币保存在客户端本地, 并随着每一次请求发送. 这样一来, 服务器收到的每一个请求都可以自我校验!

例子

如前所述的酒店, 我们知道现实中所有的酒店都是采用后者的运作模式, 因为后者的优势非常明显. 客人都是有判断能力的, 有了钥匙, 客人可以自己根据钥匙找到他的房间, 这样一来, 不仅节约了管理成本, 还能够提高安全性 (考虑数据泄漏的情况).

还有一个很好的例子可以阐述有状态和无状态的区别:

有一位象棋大师, 某天想要举办一次同时对弈多人的比赛. 如果要大师把所有人的盘面记在脑子里, 那么同时对弈的人数就不可能太多. 但是, 如果把所有盘面摆出来, 在轮到大师下的时候实时分析盘面落子, 那大师可以同时对弈的人数就可以大大提高. 两种形式的区别就在于: 前者由大师 (服务器) 管理状态, 后者由参赛者 (用户) 管理状态.

REFERENCE

Defining Stateful vs Stateless Web Services

Designing a True REST State Machine

Cookie 和 Session 有什么区别? - 知乎