`
cppmule
  • 浏览: 435535 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

使用Erlang和Yaws开发REST式的服务

 
阅读更多

使用Erlang和Yaws开发REST式的服务


看过那张很出名的“Apache vs. Yaws”图么?是不是在考虑你也应该使用Yaws了?这些图给人的第一印象是,Yaws在可伸缩性上具有难以置信的巨大优势,它可以扩展到80000个并行的连接,而 Apache只接入4000个连接后就无法继续支撑了。人们对这些图的反应存在着明显的分化,一种声音说“这些图不太可能是准确的”或者“他们一定没有正确地配置Apache”;另一种声音则完全相反,“Wow,我要尝试一下Yaws!”

无论你是否相信上面的Yaws对比图,Yaws的确是一个可靠的Web Server,可以处理动态内容。Claes Wikström使用Erlang开发了Yaws,“另一个Web Server(Yet Another Web Server)”。Erlang是一种编程语言,特别用于支持长时间运行的、并发的、高可靠的分布式系统。(要学习更多关于Erlang的知识,可以去看那本很精彩的“Programming Erlang”,它的作者是Erlang语言的创建者——Joe Armstrong。)Yaws的灵活性和Erlang的多种独一无二的特性相结合,使得它们成为了一个不可忽视的REST式的Web服务平台。如果你处理的是静态页面,去试试lighttpd或者nginx吧,但是如果你在写动态的、REST式的Web服务,那么Yaws是绝对值得尝试的。在这篇文章中,我将讲述我在使用Yaws和Erlang开发Web服务中的一些经验。

Yaws基础

Yaws提供了若干种处理动态Web内容以及支持REST式的Web服务的方法:

  • 在静态页面中嵌入Erlang代码。通过这种方法,你可以将...标签内的out/1函数直接嵌入到静态页面中。该函数包含了Erlang代码。这样的文件要以.yaws 为扩展名,从而通知Yaws处理它,并将...标签替换为执行out/1函数的结果,这正是页面应该包含的。在Erlang的术语中,out/1是元数(arity)1的函数,例如,某个带有一个参数的函数。这个参数应该是一个Yaws arg记录(record),这是一种特殊的数据结构,Yaws使用它将接收到的请求的细节传递给处理它们的代码。例如,一个arg记录可以提供请求URI、请求头、POST 数据等信息。

  • 应用程序模块(appmod)。由于Yaws的appmod,应用程序代码可以控制URI。在前面描述的方法中,Erlang代码被嵌入到静态文件中了,而这些文件的URI是由它们的路径相对于Web Server的文档根决定的。然而,有了appmod后,应用程序就会控制URI的含义,这些URI通常不会与任何文件系统上的工件有联系。Appmod 基本上都是一个导出out/1函数的Erlang模块。这些模块要在Yaws配置文件中进行配置,来关联一个URI路径元素。如果一个请求中包含了某个已注册的appmod所关联的路径元素,Yaws会调用这个模块的out/1arg记录。模块的out/1函数可以继续解释URI剩下的部分,以此来解释请求和响应目标的具体资源是什么。

  • Yaws应用程序(yapp)。appmods通常仅仅是单一的Erlang模块,Yaws yapp与此不同,它是全功能的应用程序。每个yapp都有它自己的文档根,都有它自己的appmod集。说得明确些,yapp就是Erlang/OTP 应用程序。OTP表示“Open Telecom Platform(开发电信平台)”,它是一系列历经考验的库和框架,它们为Erlang程序带来了强大的能力。OTP封装了很多构建分布式、事件驱动、高可用性系统的模式和方法。Erlang/OTP已经在现实世界中获得了证明,它们可以被用在不同的电信系统中,例如,某些系统宣称它们的宕机时间每年不过几毫秒而已。

上述这三种方法(它们的细节可以在Yaws站点上找到)都可以有效地应用在REST式的Web Service中。具体情况就要依赖于Service本身的特征了。但是根据我的经验,yapp和appmod是最好用的,因为它们提供了对Web应用程序的最大控制。

REST式的设计

既然我们打算要开发REST式的Web服务,那么首先了解一下REST的相关细节。REST的全称是“表象化状态转变(Representational State Transfer)”。Roy T. Fielding博士在他的论文中首次提出了“REST”这个术语,用它来描述一个适用于高可扩展性分布式系统(比如Web)的架构风格。HTTP本质上就是REST的一个实现。术语“表象化状态转换”是指REST式的系统通过在请求和响应之间交换资源状态的表象,来完成各种操作。例如,对于一个典型的从HTTP GET获得的Web页面来说,它就是Web资源的一个HTML表象,通过URI来标识,并由GET来触发。

开发REST式的Web服务需要注意下面几点:

  • 资源与资源标识符

  • 每种资源支持的方法

  • 数据在客户端与服务端之间交换所使用的格式

  • 状态码

  • 每个请求和响应的HTTP头

让我们把目光集中在Yaws和Erlang中,逐一地看看上面列出的几个问题。

资源标识符

设计REST式的Web服务时,需要你考虑组成服务的资源,比如如何最佳地标识它们、其中一个如何与另一个相关联。REST式的资源由URI来标识。通常,资源都拥有一个与它们自身相关的URI,同时共享一个公共的路径元素。例如,在一个基于Web的Bug追踪系统中,所有 “Phoenix”项目(一个虚构的项目)中的bug都可以在http://www.example.com/projects/Phoenix/bugs/下找到,只要指明一个bug号就可以了,比如bug 12345的URI可能就是http://www.example.com/projects/Phoenix/bugs/12345/。REST式的资源还能够提供自身状态表象内部的其他资源的URI。对于一个获得特定资源状态的用户,可以使用这个返回的URI(包含在状态表象中)来导航到整个Web应用中的其他部分上。

在Yaws中,arg记录指定了请求的URI,使用yaws_api模块提供的request_url方法可以很容易地获得它:

out(Arg) ->
     Uri = yaws_api:request_url(Arg),
     Path = string:tokens(Uri#url.path, "/"), 

一旦你得到了请求URI,那么可以像上面那样很方便地对请求路径切词,这要按照“/”进行分割就可以了。切词后可以得到路径元素的列表,它的起点是appmod的根节点。例如,假设我们将一个appmod绑定到“projects”路径元素上,完整的URI是http://www.example.com/projects/。如果一个请求URI的前缀是前面的URI,那么appmod的out/1函数从中获取一个分离的路径元素列表,代表了请求的目标资源。例如,一个URI为 http://www.example.com/projects/Phoenix/bugs/的请求,在执行过上面的一段代码后,Path 变量将保存下面的路径元素的列表:

["projects", "Phoenix", "bugs"]

分离URI的好处在于它可以简化后面进一步的转发工作,这得益于Erlang的模式匹配能力。例如,我们可以写一个函数,比如是out/2,像下面这样定义它的函数头,它就可以处理这种特殊的URI了:

out(Arg, ["projects", Project, "bugs"]) -> 
     % code to handle this URI goes here.

这个out/2函数可以处理在所有已知的项目中,所有与bug列表相关的请求。Project变量会在方法体中出现,它的值被设置为正在请求的项目的名称。支持额外的URI同样非常简单:为out/2函数添加更多的变量。如果你不喜欢out这个函数名,可以换成任意的,因为Yaws框架不会直接调用它们。

注意,正确地定义资源URI可以产生巨大的好处。利用appmod和yapp,可以非常容易地拥有一个巨大的、丰富的URI空间,因为无论是将不同的 appmod绑定到不同的URI路径元素,还是转发请求,都相当简单。Erlang的模式匹配降低了处理处理不同URI请求的难度。这与传统的非REST 式的服务在处理这种问题时的笨拙形成鲜明的对比,它们为所有服务都提供一个相同的URI。一般这个URI会指向一个脚本,它通过请求体自身的信息或者 URI查询字符串的信息来判断将请求实际转发到哪里。这种基于传统技术的URI看上去似乎有永无止境的参数,与此相比,前面所示的基于 Erlang/Yaws转发技术的URI要清晰的多。

资源方法

Web客户端可以调用的Web资源上的方法是由HTTP的动词定义的,主要包括GETPUTPOSTDELETE。但是,有些资源只能支持这些动词的一部分。当你在设计Web服务时,需要确定每种资源都支持哪些方法,记住,RFC 2616定义了每种HTTP动词期望的语义。

Yaws可以在http_request记录中找到请求方法,它可以通过arg记录很容易地获得:

Method = (Arg#arg.req)#http_request.method

它返回表示请求方法的Erlang atom,可以将它添加到模式匹配的转发方法中去。我们可以为out函数添加一个新的参数来包含请求的方法 ,于是就有了out/3

out(Arg, "GET", ["projects", Project, "bugs"]) ->
     % code to handle GET for this URI goes here.

这个out函数的变体只能够处理对每个项目的bug列表的GET请求。另一个变体可以处理POST,也许通过它来在列表中添加新的bug。如果希望只允许GETPOST请求,而拒绝其他的动作,可以再为这个URI编写一个统一处理的函数:

out(Arg, "GET", ["projects", Project, "bugs"]) ->
     % code to handle GET for this URI goes here;
 out(Arg, "POST", ["projects", Project, "bugs"]) ->
     % code to handle POST for this URI goes here;
 out(Arg, _Method, ["projects", _Project, "bugs"]) ->
     [{status, 405}].

在此,GETPOST以外的方法将会匹配第三个变体,它会返回HTTP状态码405,意味着“method not allowed”。由于MethodProject变量并未在方法中使用,所以在它们前面加下划线可以关闭编译器对此发出的警告。

就像URI转发一样,Erlang模式匹配可以让开发者很容易地将不同的HTTP动词转发到不同的函数上。

表现格式

在设计REST式的Web服务时,你需要考虑每个资源支持哪些表象。比如,Web服务资源通常都支持XML或者JSON表象。Erlang提供了xmerl library,可以创建和读取XML,Yaws提供了一个方便的JSON模块。这些都非常好用。

你可以通过请求的Accept头来判断客户端更喜欢哪种表象。这个头可以在headers记录中获得,并可以在arg记录中使用:

Accept_hdr = (Arg#arg.headers)#headers.accept

如果资源支持多种表象,你可以检查这个头,判断客户端是否指定了它希望的表象类型。如果客户端没有发送Accept头,上面代码中的Accept_hdr变量将被设置为atom undefined,你的资源可以提供任何它认为最佳的表象。如果Accept头不是空的话,服务可以解析Accept_hdr变量来判断发送哪一中资源。如果资源无法满足客户请求的表象,服务将返回HTTP状态码406,这意味着“not acceptable”,同时返回一个包含可接受格式列表的boby

case Accept_hdr of
     undefined ->
         % return default representation;
     "application/xml" ->
         % return XML representation;
     "application/json" ->
         % return JSON representation;?
     _Other ->
         Msg = "Accept: application/xml, application/json",
         Error = "Error 406",
         [{status, 406},
          {header, {content_type, "text/html"}},
          {ehtml,
           [{head, [], [{title, [], Error}]},
            {body, [],
             [{h1, [], Error},
              {p, [], Msg}]}]}]
 end. 

上面的Erlang代码首先检查 Accept_hdr的值,确定是否为application/xml或者application/json。如果是这两者之一,资源将返回一个适当的表象;如果不是,代码将返回HTTP状态码406,同时还有一个HTML文档,指明资源能够支持的表象类型。

处理预期表象的另一种方法是(你已经猜到了)将它做为另一个参数,添加到out函数中。利用这种方法,Erlang模式匹配能够确保我们的请求可以被转发到正确的函数,请求中将包含URI/method/representation。这样可以避免出现像上面那样由于case语句导致的杂乱无章的处理程序。

顺便提一句,这个例子中也出现了Yaws的ehtml类型,它是一系列的Erlang术语之一,代表一种HTML的表现方式。我发现使用ehtml是相当自然的事情,因为它后面直接是一个HTML结构体,但是它更加紧凑,而且你在编写HTML语义时,避免了很多匹配标签带来的乏味和错误。

状态码

REST式的Web服务必须返回一个正确的HTTP状态码,它们是由RFC 2616指定的。使用Yaws能够很容易地返回正确的状态:只要在out/1函数的结果中包含一个status tuple就可以了。如果你的代码没有显式地设置状态,Yaws会为你设置一个200状态,表示成功。

HTTP头

Yaws也可以很容易地获得请求头和设置回复头。我们已经看到了一个从头记录中获得Accept头的示例;获取其他请求头的方法完全一样。设置回复头只需要在回复中放置一个header tuple就可以了,如下所示:

{header, {content_type, "text/html"}}

上面的代码会将Content-type头设置为“text/html”。类似地,在前面的例子中,我们返回405状态表示“method not allowed”错误,我们也应该包含下面的头:

{header, {"Allow", "GET, POST"}} 

是Appmod,还是Yapp?

到目前为止,我们已经看到了Yaws和Erlang是如何方便地解决REST式的 Web服务中需要面对的一些关键问题的。还有一个问题,我们应该选择appmod,还是yapp呢?答案依赖于你的服务要做的事情。如果你编写的服务必须与其他后端的服务交互,那么yapp可能是最好的选择。因为它们是彻头彻尾的Erlang/OTP应用程序,它们通常都有初始化和终结函数,用来创建和关闭到后端的连接。比如,如果你的yapp是一个Erlang/OTP gen_server, 你的init/1函数可以创建gen_server框架提供给你的状态,并允许你对它进行修改。每次接收到外部到服务器的请求后都会调用init/1。另外,使用yapp的同时也可以使用appmod,因此在这两者间做选择并不是非常关键。最后,yapps可以加入到Erlang/OTP的监管树(supervision tree)中,这样监控进程会监控yapp程序,一旦失败会将它们重新启动。Erlang系统之所以可以长时间稳定地运行,监管树在其中扮演了一个很重要的角色。

这篇文章是为基于后端,而非关系数据库的REST式的Web服务量身定做的。如果你正在编写传统的、基于关系数据库的Web服务,你应该试用一下专为这类Web服务准备的Erlyweb,它也是基于Yaws和Erlang的。

结论

编写REST式的Web服务的另一个重要方面是选择恰当的编程语言。这些年来,用不同的编程语言开发的各种服务框架令人眼花缭乱,大多数很快就从人们的视野中消逝了,因为它们不能很好的解决真正的问题。Yaws和Erlang并不是专门用于提供REST式的服务的框架,不过它提供的功能比很多用其他语言开发专用于REST的框架更合适这个领域的开发。

尽管这篇文章必然无法深入Yaws、Erlang和REST式的Web服务的细节,不过希望它能够涉及到重要的主题,通过最少量的代码,提供一个解决这些问题的思路。根据我的经验,使用Yaws和Erlang构建Web应用程序非常简单,最终的代码也容易阅读、维护和扩展。

作者简介

Steve Vinoski是IEEE和ACM的成员。在过去20年里,他已经独自或与他人合作编写超过了80篇文章,各种专栏,以及一本关于分布式计算和整合的专著,在过去的6年里,他负责IEEE Internet Computing杂志的“Toward Integration”专栏。你可以给他发送邮件vinoski@ieee.org,或者访问他的blog:http://steve.vinoski.net/blog/

查看英文原文RESTful Services with Erlang and Yaws 
来自:http://www.infoq.com/cn/articles/vinoski-erlang-rest

分享到:
评论

相关推荐

    [Erlang] 网络应用开发 (Erlang 实现) (英文版)

    [奥莱理] Building Web Applications with Erlang Working with REST and Web Sockets on Yaws (E-Book) ☆ 出版信息:☆ [作者信息] Zachary Kessin [出版机构] 奥莱理 [出版日期] 2012年06月14日 [图书页数] ...

    RabbitMQ服务+Erlang语言开发包.rar

    RabbitMQ消息队列下载安装(Windows版本) rabbitmq-server-3.7.18.exe Erlang语言开发包

    Erlang开发及应用

    Erlang开发及应用

    使用erlang连接ES的客户端

    使用erlang语言实现连接ElasticSearch服务器的客户端代码

    erlang开发入门教程

    erlang是爱立信开发的程序开发语言,融合了函数式编程与面向对象编程,并行处理内建与程序语言内部,特别适合创建并发行、容错性、分布性要求比较高的软实时系统,掌握它程序员必备的一种编程技能,与它相似的语言...

    使用rebar工具开发erlang工程项目

    使用rebar工具开发erlang工程项目和发布erlang工程项目

    二郎助手erlang开发工具、erlang编辑器

    二郎助手,专为erlang语言开发的开发工具。完全免费,源代码开源(VS2005开发)。

    Erlang游戏开发

    Erlang游戏开发.ppt

    erlang开发简介

    erlang

    erlang的小型游戏服务器

    erlang单服游戏服务器,mnesia数据库,小型的游戏demo,编译可以启动,可以用来学习erlang游戏服务器相关的东西

    Erlang开发环境搭建

    Erlang开发环境搭建配置手册,让你进入erlang的世界

    yaws:Yaws Web服务器

    这是Yaws,是用Erlang编写的动态内容的Web服务器。 准备构建 获取并安装一个Erlang系统( )。 要编译Yaws,需要Erlang / OTP 18.0或更高版本。 如果您已经从github克隆了源代码,并且想使用自动工具进行构建,请...

    erlang emacs开发环境

    erlang的emacs环境搭建,其实网上有好多教程,下载地址也有,我这个是把需要的软件下载好了的,并且配置好了的,主要是方便不能下载软件的朋友。 包括.emacs.d .emacs .erlang.cookie distel-4.03.tgz Emacs-23-CvsP...

    Erlang语言开发包

    Erlang语言开发包 windows版本 安装rabbitmq必备 提供一个便利的途径,官网最新23版本,下载后需要配置环境变量

    使用erlang进行UDP测试

    使用erlang进行UDP测试,这几个是为了凑够20字。

    程序员开发erlang的资料

    java/c程序员开发erlang的资料

    erlang开发的消息中间件 RabbitMQ

    适合学习Erlang开发的同仁们研读,压缩包除服务核心之外,还包含了大量不同的网络消息协议插件,便于异构协议之间的消息交换,很有价值的东西,但是,研读具有一定难度,需要了解Erlang的OTP编程和一定的网络协议...

    Erlang 微分进化算法

    使用Erlang开发的微分进化算法。Erlang是一种函数式编程语言,非常适合于开发并行、分布式的应用

    rabbitMq和erlang安装包

    rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统。它遵循Mozilla Public License开源协议,采用 Erlang 实现的工业级的消息队列(MQ)服务器,Rabbit MQ 是建立在Erlang OTP平台上。

Global site tag (gtag.js) - Google Analytics