Wednesday, December 2, 2009

基于REST风格构建WEB应用的实践反思

江湖上有着这样一则传闻:
面试官:"请问REST是虾米?"
面试者:"哦,您放心,我是永远不知道休息的..."

我想面试官的这个问题,本文的读者应该大体是有一个概念的.不过为了行文的方便,这里本博还是将REST的核心原则摘录于下:.

  • 为所有“资源”定义标识(URI)
  • 将所有资源链接在一起
  • 使用标准方法
  • 资源多重表述
  • 无状态通信

由于公司战略需要一款SNS产品,本博有幸参与了整个开发活动,并主持了架构设计和实现.因此得以实战REST,并将前后遇到的一些问题和思考记录下来,遂成此文.


那么为啥会考虑采用REST作为系统架构风格呢?

首先,由于是一款自己运营SNS产品,根据产品规划部门制定的需求,在技术上要求我们实现大量的用户行为记录,同时还要为用户提供包含博客、相册、视频在内的web2.0“标准”应用以及支持第三方植入应用的OpenAPI体系.因此系统在安全性,并发性,稳定性,扩展性等方面均有较高的要求.
其次,由于项目团队的人员配备,开发模式,开发周期等因素的影响,敏捷开发也成为一个潜在的要求.
针对这些客观实际情况,本博考察了主流SNS的技术方案,结合自身特定,决定采用REST来实施构建:

  1. 资源URI可以有效描述系统涉及的角色(用户,及其产生的行为结果),无论是在用户群体还是在单一个用户对象.
  2. 系统对外以URL形式(对内则是URI)来与客户端通讯,这也是负载均衡得以实施的前提
  3. URI(Entity,QueryString,Method)封装了编程实体,后端程序可以有效建立Resource-Object映射,配合Key-Value缓存以及ORM等技术手段优化,可以满足对伸缩性的要求.
  4. REST提倡的HTTP操作方法封装了数据操作的CRUD
  5. URI之间的聚合(combination)有效建立起一组可复用的API体系.


实践中采用REST风格来构建项目,较为显著的带来了两个方面的提升:技术层次上,团队内推广和普及了敏捷开发,并在实践中性能成一套符合自身情况的开发流程;在HTTP协议,Web服务器,键值缓存,开发语言等方面开拓了技术视野并形成了相应的技术积累.管理层次上,由于构建REST应用的需要,将人员合理分配成应用开发,API开发等不同的小组,责权分明.但是很显然,这一架构的引入不可能是一帆风顺的,在实践中我们也遇到了很多问题,很多场景下我们也不得不去破坏学术定义(REST Anti-Pattern REST反模式):

  • 什么是资源?
        这样一个基础的命题上,保持怎样一个粒度,对API的构建乃至整个系统至关重要.是按照"语义"级别,将用户定义为/user/uid这样,还是针对系统级别,将数据表结构定义为资源.在这个问题上,内部产生了很多的思考和争议.虽然我们最终采用了类似前者的思路,但是这只是根据当时需求情况的取舍,这样的定义来带的后果是对"简单数据"CRUD的极大便利;资源交叉高复用,原子操作性较强.但是对集合类似资源(用户组等)至少在概念层次上是无力的,往往需要引用大量的SQL模板来进行封装使用,这也成为性能的一个隐患.而实际上后端开发人员80%的事情均用在这里(从整体最优来看,也是值得的).
        反之,如果我们将数据表,列这些定义成为资源的话,那么实际上我们将走到一条Meta-Data(元数据)编程的路子上.显然在逻辑层次上我们将不需要去考虑业务中角色以及相互关联的
问题.这种类似"虚拟机"(提供一套策略而不是机制)的架构风格应对复杂逻辑是绰绰有余的.但是在SNS这样一个毕竟存在大量单一应用以及"简单数据"访问的情况下,这样有似乎有点"过度设计".当然这只决定于需求和性能之间的平衡.

  • 对Method的使用
        几乎任何一份REST实践指南的资料中都指出过DELETE和PUT的模拟问题,使用GET/POST去实现这样一种模拟.
        完全的使用GET方式去实现,这么做的唯一好处,是开发便利:只需要将这样一条URL贴到浏览器的地址栏中,就可以完成测试.但是,这样的系统本身并没有把URI看成是"资源",而仅仅是
一种传递参数的字符串而已.同时这种链接一般不可加入书签,而且有“爬虫”造成非预期副作用的风险(假设你传递了一个?method=delete这样的参数).
        完全的使用POST方式去实现,这种做法被flicker等知名网站广泛使用,但实际上走回了SOAP的老路上.他不但完全忽视了REST的根本原则并且接下来也无法利用"缓存".我们的系统采
用这种方式的原因是设计上的"便捷"(:
  
  • NoSQL?
        SNS为NoSQL的思想贡献了相当大的关注力,这种思想意图使用key-value键值数据库来完全取代现在有的关系型数据库,通过键值缓存技术来提升性能.虽然如此,但NoSQL对系统的设计要求是相当之高,否则很容易就会发现构建出来的系统几乎不能满足良好的扩展性.

  • 工具与方法论
        开发初期,如何能使更多的开发人员理解REST思想,并应用于开发活动,我们提供了一个在线的调试器,当你输入URL的时候,可以查看返回数据以及相关信息.国内的Taobao开放平台也提供
了类似的沙盒环境(sandbox).推广一种工具往往比推广一种思想要容易的多.当我们意识到并不是所有人都需要明白什么是REST的时候,我们提供了一组language binding clinet library.


软件开发世界没有"银弹",试图用一种架构风格/模式去解决遇到的所有问题是不现实的.在实践中遇到的种种问题,探究他们的缘起以及解决之道,有利于加深对REST架构的理解和应用.那么,当我们 意识到这些问题,并尝试解决的时候,不妨跳出原有的思维局限,开拓眼界,引入更加符合实际情况的混合架构风格设计方案,这也是我们下一代技术产品的思路,并且有打算以开放源码的方式展现在 大家眼前,提供一个Resty的"砖头".

No comments:

Post a Comment