Archive for 一月, 2013

对于Rest框架Cache的思考

HTTP 前端cache分为过期和验证两种,前者主要指Expire,后者指Etag或last modify time。

先说Etag,在分布式web server的情况下,即使针对静态文件如图片、CSS、JS等,也需要关闭Etag,因为Apache之类的服务器会根据文件的inode-size-timestamp计算Etag,而不同web server的inode是肯定不同的,所以ETag的效果在分布式时会大大减弱。

回到Rest框架的cache设计上,需要实现Etag吗?框架的通用性,使它不可能关注到资源的变化,也不可能作为资源的唯一写入口,所以它不可能直接提供检测资源变化的功能,只有在真正获取到资源数据,并同上一次获取到的数据进行比对后,才可以判断资源是否发生变化。

那是否提供钩子函数,让应用层实现自己的检测方法呢?那么该方法需要满足:

  1. 若Etag发生变化,该方法可以返回资源数据,避免重复请求;
  2. 若Etag不变化,该方法可以标识结果,使框架得以发送304响应。

为了以上两个目的,且基于该方法需要实现:

  1. 直接或者间接获取到资源数据
  2. 获取上一次资源或者资源签名(支持分布式,所以针对PHP应用来说,最好使用第三方缓存如memcached)
  3. 若需要,则计算本次资源签名,进行比较
  4. 若需要,则更新资源或者资源签名

那么,为了一个Etag,实现这些复杂的钩子,有没有必要?是否优雅?有没有更好的选择呢?

个人的感觉是,类似memcached,它采用了简单的超时机制,就可以满足大部分的缓存需求,那我们的Rest框架,是否也采用Expire和LastModifyTime就ok了呢?

======================

本文希望能够使具备几年互联网开发背景的工程师,如我自己,能够切实了解什么是Rest,实现一个Rest应用或者框架,需要考虑哪些事情。

更重要的是,列出更多的参考资源,供进一步研究。

名词解释

Rest和Restful:Representational state transfer (REST) is a style of software architecture. As described in a dissertation by Roy Fielding, REST is an “architectural style” that basically exploits the existing technology and protocols of the Web.RESTful is typically used to refer to web services implementing such an architecture.即Rest是架构,Restful是实现。

Rest与WebService:Rest与SOAP一样,是WwebService的一种实现模型。

Rest架构的特征

Rest首先是遵从HTTP协议的,在此基础上,有4个特征:

  • 显式地使用 HTTP 方法。
  • 无状态。
  • 公开目录结构式的 URI。
  • 传输 XML、JavaScript Object Notation (JSON),或同时传输这两者。

1. 显示的使用HTTP方法。

我们熟悉的HTTP方法有GET/POST,偶尔还会用到HEAD和OPTION,而Rest明确提出还须使用HTTP协议定义的PUT/DELETE。

即使用GET方法获取资源,且不改变资源属性(所谓的幂等性);使用POST方法新建资源,比如创建用户等;使用UPDATE方法修改资源属性;使用DELETE方法删除资源。这些方法间相互不可越界。

既然是遵从HTTP方法做资源的访问,对称的,资源的返回也需要遵从HTTP协议。对于Body体的讨论会放到第四点里,这里先讨论HTTP State Code。一些经常被提到的候选Code如下,个人感觉一个简单的实现,能够标识正常和出错(对于常见的错误如参数错误可以加以特殊区别)即可。

200 OK  请求已成功,请求所希望的响应头或数据体将随此响应返回。

201 Created请求已经被实现,而且有一个新的资源已经依据请求的需要而创建,且其URI已经随Location头信息返回。假如需要的资源无法及时创建的话,应当返回’202 Accepted‘。

204 No Content  服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。响应可能通过实体头部的形式,返回新的或更新后的元信息。如果存在这些头部信息,则应当与所请求的变量相呼应。如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息应当被应用到用户浏览器活动视图中的文档。由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾。

301 Moved Permanently被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。新的永久性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。如果这不是一个GET或者HEAD请求,因此浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。注意:对于某些使用HTTP/1.0协议的浏览器,当它们发送的POST请求得到了一个301响应的话,接下来的重定向请求将会变成GET方式。

400 Bad Request由于包含语法错误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。

401 Unauthorized当前请求需要用户验证。该响应必须包含一个适用于被请求资源的WWW-Authenticate信息头用以询问用户信息。客户端可以重复提交一个包含恰当的Authorization头信息的请求。如果当前请求已经包含了Authorization证书,那么401响应代表着服务器验证已经拒绝了那些证书。如果401响应包含了与前一个响应相同的身份验证询问,且浏览器已经至少尝试了一次验证,那么浏览器应当向用户展示响应中包含的实体信息,因为这个实体信息中可能包含了相关诊断信息。参见RFC 2617

410 Gone被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。这样的状况应当被认为是永久性的。如果可能,拥有链接编辑功能的客户端应当在获得用户许可后删除所有指向这个地址的引用。如果服务器不知道或者无法确定这个状况是否是永久的,那么就应该使用404状态码。除非额外说明,否则这个响应是可缓存的。410响应的目的主要是帮助网站管理员维护网站,通知用户该资源已经不再可用,并且服务器拥有者希望所有指向这个资源的远端连接也被删除。这类事件在限时、增值服务中很普遍。同样,410响应也被用于通知客户端在当前服务器站点上,原本属于某个个人的资源已经不再可用。当然,是否需要把所有永久不可用的资源标记为’410 Gone’,以及是否需要保持此标记多长时间,完全取决于服务器拥有者。

2. 无状态

HTTP本身是无状态的,但是WebService,尤其是复杂的服务,很可能与事务挂钩,如果一味要求无状态,会不会导致无法实现事务一致性?那么是将一组事务封装在一个资源里,还是拆分为细粒度可复用的资源,通过分布式事务去保证呢?请求方需要怎样标识事务的开启、回滚和提交?如果请求方中断了操作序列会怎样?

3. 公开目录结构式的 URI。

出于易用性的考虑。被信任的访问者应该能够看到可用资源,并了解能够通过CRUD方法操控这些资源。从而使调用者的工作量和理解成本最小化。

4. 传输 XML、JavaScript Object Notation (JSON),或同时传输这两者。

前面讨论过返回的HTTP Header里的state Code,这里描述的是HTTP Body的内容。Rest框架的调用者可能是javascript,当然也可能是一些服务器端语言,所以这里选择了最流行的两种格式,并且要求可以通过 Accept Header的内容协商机制,由调用者指定可接受的格式。

实现Rest框架的步骤

 

参考资料