看过那张很出名的“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的函数,例如,某个带有一个参数的函数。这个参数应该是一个Yawsarg
记录(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/1
arg
记录。模块的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的动词定义的,主要包括GET
、PUT
、POST
、DELETE
。但是,有些资源只能支持这些动词的一部分。当你在设计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。如果希望只允许GET
和POST
请求,而拒绝其他的动作,可以再为这个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}].
在此,GET
和POST
以外的方法将会匹配第三个变体,它会返回HTTP状态码405
,意味着“method not allowed”。由于Method
和Project
变量并未在方法中使用,所以在它们前面加下划线可以关闭编译器对此发出的警告。
就像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
相关推荐
本期《架构师》杂志特别专题深入浅出地介绍了REST(Representational State Transfer)这一网络架构风格,包括使用Erlang和YAWS开发REST式服务的方法、星巴克REST案例分析以及解答关于REST的常见疑惑,为读者提供了...
在本文中,我们将介绍如何使用Erlang开发一个简单的游戏服务器,并讨论其优点和实现细节。 Why Erlang? Erlang是一种轻量级的语言,非常适合开发高性能的服务器应用程序。其主要优点有: * 轻量级process:...
[奥莱理] Building Web Applications with Erlang Working with REST and Web Sockets on Yaws (E-Book) ☆ 出版信息:☆ [作者信息] Zachary Kessin [出版机构] 奥莱理 [出版日期] 2012年06月14日 [图书页数] ...
Erlang是一种面向并发的、函数式的编程语言,特别适合构建高可用、容错性强的分布式系统。文件“otp_win64_22.1.exe”就是Erlang OTP(Open Telephony Platform)的Windows 64位版本,它是Erlang运行时系统的基石,...
总的来说,这个Erlang小型游戏服务器项目涵盖了游戏服务器开发的核心技术,包括Erlang的并发编程、Mnesia数据库的使用、协议设计以及版本控制和错误检测。对于想要学习Erlang游戏服务器开发的人来说,这是一个很好的...
在Yaws上启动Web服务、服务静态文件(Serving Static Files)、编译、加载和运行代码以及集群化Yaws(Clustering Yaws)是构建Erlang Web应用的关键步骤。 4. 在构建Web应用时,需要考虑系统架构(System ...
在游戏开发领域,由于其并发处理能力和强大的故障恢复机制,Erlang常被用来构建可扩展、高可用性的游戏服务器。Ranch库提供了一种简单的方式来管理多个并发连接,并且它的API设计得简洁明了,使得开发者可以快速地...
3. **Web开发**:Erlang框架如YAWS和Chicago Boss为构建高性能Web应用提供了可能。 **学习资源与进阶** - 《Erlang编程》一书是学习Erlang的入门经典,适合初学者阅读。 - OTP(Open Telecom Platform)是Erlang的...
Erlang是一种函数式编程语言,以其并发性、容错性和热代码升级能力而闻名,而RabbitMQ则是基于Erlang构建的一个开源消息代理,用于实现应用程序之间的异步通信。 **Erlang** Erlang由瑞典电信设备制造商Ericsson...
在《Erlang和OTP实战》中,作者详细讲解了如何使用Erlang的语法和函数式编程思想,以及如何利用OTP进行系统设计。书中可能涵盖以下知识点: 1. Erlang的基础语法:包括模式匹配、递归、过程(process)和数据类型等...
4. **热代码替换(Hot Code Swapping)**:Erlang允许在运行时更新和替换模块代码,无需停止整个系统,这对于持续改进和修复在线服务非常有用。 5. **Jinterface**:Jinterface是Erlang和Java之间的互操作库,它...
6. **完全免费**:对个人和商业用户都免费,无任何使用限制,这使得更多的人能够接触到Erlang开发并提高生产力。 在提供的压缩包文件“ErlangIDE”中,包含了二郎助手的安装程序或者源代码。通过安装这个工具,...
ERLANG是一种为并发、分布式和容错系统设计的函数式编程语言,因其在实时系统和大规模并发处理中的优秀性能而被广泛应用于游戏服务器开发。这份压缩包包含了这两款游戏的Erlang服务器源码,为深入理解游戏服务器的...
Erlang是一种并发性极强、容错性出色的函数式编程语言,而RabbitMQ是基于Erlang开发的开源消息代理和队列服务器。 【Erlang】 Erlang由爱立信在1986年为电信应用设计,其设计目标是处理大规模并发连接和高可用性。...
本文将指导读者如何在Windows平台下搭建Erlang开发环境,包括安装Erlang OTP、Emacs W32、Distel等工具,并配置Erlang mode和Distel。 一、安装Erlang OTP Erlang OTP是Erlang的官方实现,提供了完整的开发环境。 ...
根据给出的文件信息,我们可以...以上所述内容提供了对Erlang在Web应用开发中角色的深刻理解,特别是使用Yaws Web服务器的相关知识和实践。这些知识对于想要深入学习Erlang在Web开发中应用的开发者来说是非常重要的。
Erlang开发及应用
在IT领域,Erlang是一种面向并发的、函数式编程语言,常用于构建高可用性和分布式系统,如电信、银行和互联网服务等。Elasticsearch(简称ES)则是一款流行的开源全文搜索引擎,广泛应用于大数据分析和实时搜索。...
Erlang是一种函数式编程语言,由爱立信在1986年开发,主要用于构建高可用性、容错性和并发性的分布式系统。"Introducing Erlang"是Simon St. Laurent撰写的一本入门级教程,旨在帮助初学者理解和掌握Erlang的核心...