Erlang cowboy 入门参考

cheungmine,2014-10-28

本文翻译自:

http://ninenines.eu/docs/en/cowboy/HEAD/guide/getting_started/

我没有按原文逐句翻译。仅仅是我自己的理解,力求简单明了。本文程序在RHEL6.4上写成并运行,参考下面的文章,安装Erlang:

erlang在redhat上的安装

1 引言

Erlang不仅仅是一门语言,更是一个操作平台。Erlang开发者很少写单独的模块,而是写库和程序,然后把它们打包在一起进行发布。一个产品发布包含Erlang虚拟机和所有用来运行的程序,因此可以直接成为产品。Cowboy是Erlang编写的WebServer,提供Http,Https,WebSocket,TCP等各种高性能服务框架。本文将说明如何安装Cowboy,写第一个程序并发布。通过阅读本文,读者可以了解发布你的第一个Cowboy程序的全部内容。

2 开始第一个产品

当我们要开发一个程序(产品),我们希望用一个工程去管理它。代码构建,打包,发布等一系列行为。做Java开发使用的是Eclipse+Maven+Ant等工具。做Erlang开发可以使用Gnu Make。由于每个工具所完成的功能都类似,因此推荐使用下面这个Make模板。

https://github.com/ninenines/erlang.mk

下面开始我们的第一个Erlang工程。创建一个hello_erlang的目录,进入这个目录:

$ mkdir hello_erlang && cd hello_erlang

下载erlang.mk文件。这是erlang工程的构建脚本。关于这个脚本的细节可以在各位成为erlang专家的时候去理解。

$ wget --no-check-certificate https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk

之后我们会看到目录下多了一个文件:erlang.mk。然后我们就可以生成我们的程序,同时生成发布版本(bootstrap,bootstrap-rel是target):

$ make -f erlang.mk bootstrap bootstrap-rel

现在就可以构建我们的工程,生成发布版本:

$ make

这个命令会花费较长时间执行完毕。然后启动运行产品程序:

$ ./_rel/hello_erlang_release/bin/hello_erlang_release console

输入下面的命令可以看到运行的进程,其中包括:hello_erlang_sup。这个是我们的程序的督程(supervisor)。

(hello_erlang@127.0.0.1)1> i().

现在这个程序什么也没做,接下来我们给它添加Cowboy依赖,然后把它变成一个简单的HelloWorld程序。整个过程就是如下样子:

[root@rhel64-origin workspace]# mkdir hello_erlang
[root@rhel64-origin workspace]# cd hello_erlang/
[root@rhel64-origin hello_erlang]# ls
[root@rhel64-origin hello_erlang]# wget --no-check-certificate https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk
--2014-10-28 17:42:55--  https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk
Resolving raw.githubusercontent.com... 103.245.222.133
Connecting to raw.githubusercontent.com|103.245.222.133|:443... connected.
WARNING: certificate common name “www.github.com” doesn’t match requested host name “raw.githubusercontent.com”.
HTTP request sent, awaiting response... 200 OK
Length: 20522 (20K) [text/plain]
Saving to: “erlang.mk”

100%[======================================================>] 20,522      --.-K/s   in 0.04s

2014-10-28 17:42:55 (473 KB/s) - “erlang.mk” saved [20522/20522]

[root@rhel64-origin hello_erlang]# ls
erlang.mk
[root@rhel64-origin hello_erlang]# make -f erlang.mk bootstrap bootstrap-rel
[root@rhel64-origin hello_erlang]# make
 ERLC   hello_erlang_app.erl hello_erlang_sup.erl
 APP    hello_erlang.app.src
 GEN    distclean-relx-rel
--2014-10-28 17:43:08--  https://github.com/erlware/relx/releases/download/v1.0.2/relx
Resolving github.com... 192.30.252.131
Connecting to github.com|192.30.252.131|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://s3.amazonaws.com/github-cloud/releases/9900098/f6fcb596-e38d-11e3-9e8b-c953e4c1e42a?response-content-disposition=attachment%3B%20filename%3Drelx&response-content-type=application/octet-stream&AWSAccessKeyId=AKIAISTNZFOVBIJMK3TQ&Expires=1414489636&Signature=adyKnn8IPqauqWSnsXhJTc2vBio%3D [following]
--2014-10-28 17:43:12--  https://s3.amazonaws.com/github-cloud/releases/9900098/f6fcb596-e38d-11e3-9e8b-c953e4c1e42a?response-content-disposition=attachment%3B%20filename%3Drelx&response-content-type=application/octet-stream&AWSAccessKeyId=AKIAISTNZFOVBIJMK3TQ&Expires=1414489636&Signature=adyKnn8IPqauqWSnsXhJTc2vBio%3D
Resolving s3.amazonaws.com... 54.231.244.0
Connecting to s3.amazonaws.com|54.231.244.0|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 743699 (726K) [application/octet-stream]
Saving to: “/root/workspace/hello_erlang/relx”

100%[======================================================>] 743,699      147K/s   in 5.0s

2014-10-28 17:43:19 (145 KB/s) - “/root/workspace/hello_erlang/relx” saved [743699/743699]

===> Starting relx build process ...
===> Resolving OTP Applications from directories:
          /root/workspace/hello_erlang/ebin
          /usr/local/lib/erlang/lib
===> Resolving available OTP Releases from directories:
          /root/workspace/hello_erlang/ebin
          /usr/local/lib/erlang/lib
===> Resolved hello_erlang_release-1
===> Including Erts from /usr/local/lib/erlang
===> release successfully created!

3 cowboy安装

为了使我们的hello_erlang使用cowboy,我们需要修改2个文件:Makefile和src/hello_erlang.app.src。修改后的Makefile如下:

PROJECT = hello_erlang
DEPS = cowboy
include erlang.mk

我们在src/hello_erlang.app.src文件中添加cowboy,这让我们的程序发布时自动将cowboy打包。有些仅仅是开发阶段的依赖不必要添加在这里(applications[...])。修改后的文件如下:

{application, hello_erlang, [
        {description, "Hello Erlang!"},
        {vsn, "0.1.0"},
        {modules, []},
        {registered, []},
        {applications, [
                kernel,
                stdlib,
                cowboy
        ]},
        {mod, {hello_erlang_app, []}},
        {env, []}
]}.

现在我们运行make命令,cowboy就被自动加入到产品中,当然它什么也没做。输出如下:

[root@rhel64-origin hello_erlang]# make
--2014-10-28 17:53:58--  https://raw.githubusercontent.com/ninenines/erlang.mk/master/packages.v2.tsv
Resolving raw.githubusercontent.com... 103.245.222.133
Connecting to raw.githubusercontent.com|103.245.222.133|:443... connected.
WARNING: certificate common name “www.github.com” doesn’t match requested host name “raw.githubusercontent.com”.
HTTP request sent, awaiting response... 200 OK
Length: 7115 (6.9K) [text/plain]
Saving to: “/root/workspace/hello_erlang/.erlang.mk.packages.v2”

100%[======================================================>] 7,115       --.-K/s   in 0.001s

2014-10-28 17:54:06 (12.6 MB/s) - “/root/workspace/hello_erlang/.erlang.mk.packages.v2” saved [7115/7115]

Initialized empty Git repository in /root/workspace/hello_erlang/deps/cowboy/.git/
remote: Counting objects: 7063, done.
remote: Total 7063 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (7063/7063), 5.26 MiB | 530 KiB/s, done.
Resolving deltas: 100% (4198/4198), done.
make[1]: Entering directory `/root/workspace/hello_erlang/deps/cowboy'
DEPPKG=$(awk 'BEGIN { FS = "\t" }; $1 == "cowlib" { print $2 " " $3 " " $4 }' /root/workspace/hello_erlang/.erlang.mk.packages.v2;) VS=$(echo $DEPPKG | cut -d " " -f1); REPO=$(echo $DEPPKG | cut -d " " -f2); COMMIT=$(echo $DEPPKG | cut -d " " -f3);        if [ "$VS" = "git" ]; then git clone -n -- $REPO /root/workspace/hello_erlang/deps/cowlib; cd /root/workspace/hello_erlang/deps/cowlib && git checkout -q $COMMIT; else exit 78; fi
Initialized empty Git repository in /root/workspace/hello_erlang/deps/cowlib/.git/
remote: Counting objects: 202, done.
remote: Total 202 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (202/202), 87.64 KiB | 100 KiB/s, done.
Resolving deltas: 100% (119/119), done.
DEPPKG=$(awk 'BEGIN { FS = "\t" }; $1 == "ranch" { print $2 " " $3 " " $4 }' /root/workspace/hello_erlang/.erlang.mk.packages.v2;) VS=$(echo $DEPPKG | cut -d " " -f1); REPO=$(echo $DEPPKG | cut -d " " -f2); COMMIT=$(echo $DEPPKG | cut -d " " -f3);         if [ "$VS" = "git" ]; then git clone -n -- $REPO /root/workspace/hello_erlang/deps/ranch; cd /root/workspace/hello_erlang/deps/ranch && git checkout -q $COMMIT; else exit 78; fi
Initialized empty Git repository in /root/workspace/hello_erlang/deps/ranch/.git/
remote: Counting objects: 905, done.
Receiving objects: 100% (905/905), 288.85 KiB | 170 KiB/s, done.
remote: Total 905 (delta 0), reused 0 (delta 0)
Resolving deltas: 100% (532/532), done.
make[2]: Entering directory `/root/workspace/hello_erlang/deps/cowlib'
 ERLC   cow_http_te.erl cow_http.erl cow_http_hd.erl cow_date.erl cow_mimetypes.erl cow_multipart.erl cow_qs.erl cow_cookie.erl cow_spdy.erl
 APP    cowlib.app.src
make[2]: Leaving directory `/root/workspace/hello_erlang/deps/cowlib'
make[2]: Entering directory `/root/workspace/hello_erlang/deps/ranch'
 ERLC   ranch_acceptors_sup.erl ranch_conns_sup.erl ranch_server.erl ranch_tcp.erl ranch.erl ranch_acceptor.erl ranch_listener_sup.erl ranch_app.erl ranch_protocol.erl ranch_ssl.erl ranch_sup.erl ranch_transport.erl
 APP    ranch.app.src
make[2]: Leaving directory `/root/workspace/hello_erlang/deps/ranch'
 ERLC   cowboy_router.erl cowboy_rest.erl cowboy_http_handler.erl cowboy_sup.erl cowboy_websocket_handler.erl cowboy_static.erl cowboy_sub_protocol.erl cowboy_websocket.erl cowboy.erl cowboy_app.erl cowboy_handler.erl cowboy_loop_handler.erl cowboy_protocol.erl cowboy_bstr.erl cowboy_clock.erl cowboy_http.erl cowboy_spdy.erl cowboy_req.erl cowboy_middleware.erl
 APP    cowboy.app.src
make[1]: Leaving directory `/root/workspace/hello_erlang/deps/cowboy'
 APP    hello_erlang.app.src
 GEN    distclean-relx-rel
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
          /root/workspace/hello_erlang/ebin
          /root/workspace/hello_erlang/deps
          /usr/local/lib/erlang/lib
===> Resolving available OTP Releases from directories:
          /root/workspace/hello_erlang/ebin
          /root/workspace/hello_erlang/deps
          /usr/local/lib/erlang/lib
===> Resolved hello_erlang_release-1
===> Including Erts from /usr/local/lib/erlang
===> release successfully created!

4 监听连接

当一个客户端请求到来,我们需要根据请求内容将请求分发(dispatch)到处理器(handler)。因此我们需要一个路由表(dispatch list),cowboy根据路由表分发请求到对应的处理模块程序。然后我们让cowboy开始监听连接。

编辑Erlang程序源代码文件:src/hello_erlang_app.erl,修改start/2函数,最后的src/hello_erlang_app.erl如下:

-module(hello_erlang_app).
-behaviour(application).

-export([start/2]).
-export([stop/1]).

start(_Type, _Args) ->
	Dispatch = cowboy_router:compile([
		{'_', [{"/", hello_handler, []}]}
	]),
	cowboy:start_http(my_http_listener, 100, [{port, 8080}],
		[{env, [{dispatch, Dispatch}]}]
	),
	hello_erlang_sup:start_link().	

stop(_State) ->
	ok.

现在我们把根路由"/"定义到hello_handler,下面我们就需要为此写这个处理器。

5 处理请求

cowboy具有处理各种请求的特色,例如:REST,WebSocket等。这里我们使用一个简单的HTTP处理器。我们使用处理器模板生成这个处理器:

$ make new t=cowboy_http n=hello_handler

这将创建src/hello_handler.erl代码文件。打开它,修改成下面的样子(只修改了init/3):

-module(hello_handler).
-behaviour(cowboy_http_handler).

-export([init/3]).
-export([handle/2]).
-export([terminate/3]).

-record(state, {
}).

init(_, Req, Opts) ->
	Req2 = cowboy_req:reply(200,
		[{<<"content-type">>, <<"text/plain">>}],
		<<"Hello Erlang!">>,
		Req),
	{ok, Req2, Opts}.

handle(Req, State=#state{}) ->
	{ok, Req2} = cowboy_req:reply(200, Req),
	{ok, Req2, State}.

terminate(_Reason, _Req, _State) ->
	ok.

6 构建并运行

$ make
$ ./_rel/hello_erlang_release/bin/hello_erlang_release console

在浏览器中打开:http://your-ip-addr:8080/,显示如下:

7 总结

cowboy这个框架和nodejs很像。erlang的语法有点生疏,但是erlang比nodejs不知道强多少倍。erlang是C写的,nodejs的底层是C++,可见多数C++写的产品最后都不如C写的,这是偶然么?“一只鸟长得像鸭子,叫声像鸭子,走路也像鸭子,那它就是鸭子!”

Erlang cowboy 入门参考的更多相关文章

  1. Erlang cowboy 入门参考之现代Web的发展历史

    Erlang cowboy 入门参考之现代Web发展史 原文: http://ninenines.eu/docs/en/cowboy/1.0/guide/modern_web/ 让我回顾一下web技术 ...

  2. Erlang cowboy websocket 服务器

    Erlang cowboy websocket 服务器 原文见于: http://marcelog.github.io/articles/erlang_websocket_server_cowboy_ ...

  3. Erlang cowboy 处理不规范的客户端

    Erlang cowboy 处理不规范的客户端 Cowboy 1.0 参考 本章: Dealing with broken clients 存在许多HTTP协议的实现版本.许多广泛使用的客户端,如浏览 ...

  4. Erlang cowboy 架构

    Erlang cowboy Architecture架构 Erlang cowboy参考: http://ninenines.eu/docs/en/cowboy/1.0/guide/ 本章Archit ...

  5. erlang分布式入门(一)-ping pong

    erlang分布式入门(一)-ping pong 测试环境和http://willvvv.iteye.com/blog/1523918 一样,192.168.0.182(centos-182)和192 ...

  6. Erlang cowboy 处理不规范的client

    Erlang cowboy 处理不规范的client Cowboy 1.0 參考 本章: Dealing with broken clients 存在很多HTTP协议的实现版本号. 很多广泛使用的cl ...

  7. Erlang cowboy 处理简单的HTTP请求

    Erlang cowboy 处理简单的HTTP请求 原文出自: Handling plain HTTP requests 处理请求的最简单的方式是写一个简单的HTTP处理器.它的模型参照Erlang/ ...

  8. Erlang cowboy routing 路由

    Erlang cowboy routing 路由 本文译自: http://ninenines.eu/docs/en/cowboy/1.0/guide/routing/ Routing 默认情况下,C ...

  9. Erlang cowboy http request生命周期

    Erlang cowboy http request生命周期 翻译自: http://ninenines.eu/docs/en/cowboy/1.0/guide/http_req_life/ requ ...

随机推荐

  1. MPAndroidChart的K线图上添加均线

    MPAndroidChart的K线图上添加均线 效果图 均线计算方法: 通常说的5日均线,10日均线,其实就是根据当前K线节点的时间维度来说的,当前每个节点代表一天,那么上面的均线就叫做日均线(几日均 ...

  2. 最简单的基于Flash的流媒体示例:RTMP推送和接收(ActionScript)

    ===================================================== Flash流媒体文章列表: 最简单的基于Flash的流媒体示例:RTMP推送和接收(Acti ...

  3. Android初级教程获取手机系统联系人信息

    在手机内部,对联系人信息存在对应的数据库.我们创建的而联系人信息都存在这张表中.如下是对数据库的截图,我已经对表和应该注意的地方做了红笔标注: 好了,现在可以根据数据库里面的数据来写代码了. 代码如下 ...

  4. Request中Attribute 和 Parameter 的区别

    Attribute 和 Parameter 的区别 (1)HttpServletRequest类有setAttribute()方法,而没有setParameter()方法 (2)当两个Web组件之间为 ...

  5. Java项目源码为什么要做代码混淆(解释的很好)

    代码混淆,是将计算机程序的代码转换成一种功能上等价,但是难于阅读和理解的形式的行为.代码混淆可以用于程序源代码,也可以用于程序编译而成的中间代码.执行代码混淆的程序被称作代码混淆器.目前已经存在许多种 ...

  6. 使用Python做简单的字符串匹配

    由于需要在半结构化的文本数据中提取一些特定格式的字段.数据辅助挖掘分析工作,以往都是使用Matlab工具进行结构化数据处理的建模,matlab擅长矩阵处理.结构化数据的计算,Python具有与matl ...

  7. Android开发模式之MVC,MVP和MVVM的简单介绍与区别

    相信大家对MVC,MVP和MVVM都不陌生,作为三个最耳熟能详的Android框架,它们的应用可以是非常广泛的,但是对于一些新手来说,可能对于区分它们三个都有困难,更别说在实际的项目中应用了,有些时候 ...

  8. Java创建二叉搜索树,实现搜索,插入,删除操作

    Java实现的二叉搜索树,并实现对该树的搜索,插入,删除操作(合并删除,复制删除) 首先我们要有一个编码的思路,大致如下: 1.查找:根据二叉搜索树的数据特点,我们可以根据节点的值得比较来实现查找,查 ...

  9. 我眼中的Linux设备树(四 中断)

    四 中断中断一般包括中断产生设备和中断处理设备.中断控制器负责处理中断,每一个中断都有对应的中断号及触发条件.中断产生设备可能有多个中断源,有时多个中断源对应中断控制器中的一个中断,这种情况中断产生设 ...

  10. JSON 的数据转换格式(DataTable或DataSet) -善良公社项目

    这两天在使用JqueryEasyUI框架绑定数据并实现自动分页时,由于框架的限制需要使用Json数据的来传递与获取数据: JSON的全称是JavaScript Object Notation, 是一种 ...