RESTful API 设计最佳实践(3)

来源:互联网 发布:淘宝卖家手机必备软件 编辑:程序博客网 时间:2024/04/29 20:38

RESTful API 设计最佳实践(3)

一、无状态和有状态

无状态服务,是REST风格服务的核心约束。无状态指的是:处理请求所需要的状态信息都放在请求里面(如,放在URI路径、查询参数、body以及Header中等),而不是存放在服务器端。服务器端从请求中获取到相关状态信息,对请求进行处理之后,将需要返回的状态信息放在诸如body、header中,返回给客户端。无状态服务,是不要求请求有先后次序的。

相反,对于有状态服务,它通常是用来处理事务的,一般会在服务器端存储会话信息,比如用session,如购物时点击“加入购物车”,再点击“提交订单”,再“付款”,这些请求都是要求有先后顺序的,服务器端会将每一步的信息存储下来,汇总在一起后再进行最终的处理。

二、无状态服务的优点

无状态主要用来解决服务端的两大问题:一是提升 水平伸缩性 ,二是减轻服务端的 压力和风险
就拿web开发中常见的session举例。

1. 提升水平伸缩性

在服务器端存储session,当用户量变大时,一台服务器已满足不了需求,因此需要部署多台服务器以分担访问压力(负载均衡)。但不管部署多少台服务器,对于用户体验来说,应该是和一台服务器是一样的。这就要求每台服务器中,都存有一份session,且所有的session内容应该是一致的,这就带来了集群应用中一致性问题。当然,你可以通过访问路由策略指定某些用户访问某写特定的服务器,这样也可以在使用session的同时,也保证服务的水平扩展性,但这样也带来了会话粘连问题(水平收缩问题):高峰期扩充了100台服务器,高峰过后过后,本来1台服务器就可支撑起来的,其他100台服务器可以释放出来给其他服务用,但由于这100台服务器上都粘连了一小群用户,而使其无法回收。 而使用无状态服务,服务器端是不需要存储状态信息的,就像是一根排水管,在用水高峰时,可以并排接一根、两根、n根;用水低谷时,可以将多余的水管撤掉用作他处,你根本不需要维护这些水管间的任何关系。

2. 降低服务器端的风险和压力

这也很易理解,如果服务器崩溃,session中的内容就会消失,即使服务重启后,用户也可能就会看到一系列错误的交互界面或过程。为此,可能就需要对session中的内容进行持久化,服务器的实现会变得更加复杂。另外,session存在内存中,需要耗费硬件资源,在高并发和大流量的情况下,维持session的高效和稳定也是一大难点。种种这些,均是保存访问状态所带来的,反之,不存储状态信息,也就没有这些问题了。

三、如何用无状态服务实现事务场景

比如上面提到的购物流程,下面两种方案可供参考:

(1)将原本在服务器端要保存的本次请求的发送过来的信息,放到响应消息中,再返回给客户端(比如放到客户端cookie中或者隐藏表单中),然后客户端再下一次请求时再一并带过来,如此反复。这种方案比较消耗带宽,如果消息体太大,则可能不适合。

(2)利用分层系统,抽取出一个独立的session服务,专门存储状态信息的,业务服务则为无状态的。**这种方案其实是将风险和压力转移到了session服务器。**session服务并非是REST风格服务,所以如果要打造一整套REST风格架构,这种解决方案个人感觉是不可行的。

选择何种解决方案,还需要根据具体业务场景具体讨论。除了上面所说的一定要将“有状态”改为“无状态”的方案,在实际应用中,我觉得有些是可以妥协的。

四、一个“违反”无状态约束的例子

比如我们经常会做的“防刷”功能,在服务器端要记住某个用户或某个IP上一次访问的相关信息(访问时间、用户ID、token或IP地址等),这种做法从理论上是违反了REST的“无状态”约束,但是它对我们享用REST服务架构的优点是没有任何阻碍的:
(1)对水平伸缩性没有影响:因为防刷就是用来保护单台服务器的,与其他服务没有关联,不需要考虑数据一致性,也不需要考虑用户粘连,因为它与具体业务逻辑没有关联。所以,对水平伸缩性没有任何影响。
(2)对于服务器端的压力和风险:内存中只是维护了一张用户最后访问时间的表,占用内存本身应该不会很大,而且可以定时清理超时的数据,所以几乎不用考虑内存压力。其他风险呢?一旦服务器崩溃,这部分信息丢掉了,也没有任何关系,对用户、服务器端本身都没有任何影响。

在这种情况下,我们完全可以违反这条约束,对我们构建一个优秀的REST服务没有任何影响。在这里,又让我想起一句话:脱离具体业务场景的架构都是耍流氓。确实很有道理,但当你设计的架构确实很烂,千万别拿这就话当挡箭牌。

五、后续

这篇博客主要讲REST六个约束中的“无状态”约束详细介绍了一下,在下两篇博文中,将主要介绍REST六个约束中的“统一接口”约束。

0 0
原创粉丝点击