Erlang_RabbitMQ_PostGres
Erlang_RabbitMQ_PostGres
http://www.nsbeta.info/archives/200
http://www.nsbeta.info/archives/206
(一)基本概念
RabbitMQ是流行的开源消息队列系统,用erlang语言开发。我曾经对这门语言挺有兴趣,学过一段时间,后来没坚持。RabbitMQ是AMQP(高级消息队列协议)的标准实现。如果不熟悉AMQP,直接看RabbitMQ的文档会比较困难。不过它也只有几个关键概念,这里简单介绍。
RabbitMQ的结构图如下:
几个概念说明:
Broker:简单来说就是消息队列服务器实体。
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
producer:消息生产者,就是投递消息的程序。
consumer:消息消费者,就是接受消息的程序。
channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
消息队列的使用过程大概如下:
(1)客户端连接到消息队列服务器,打开一个channel。
(2)客户端声明一个exchange,并设置相关属性。
(3)客户端声明一个queue,并设置相关属性。
(4)客户端使用routing key,在exchange和queue之间建立好绑定关系。
(5)客户端投递消息到exchange。
exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
exchange也有几个类型,完全根据key进行投递的叫做Direct交换机,例如,绑定时设置了routing key为”abc”,那么客户端提交的消息,只有设置了key为”abc”的才会投递到队列。对key进行模式匹配后进行投递的叫做Topic交换机,符号”#”匹配一个或多个词,符号”*”匹配正好一个词。例如”abc.#”匹配”abc.def.ghi”,”abc.*”只匹配”abc.def”。还有一种不需要key的,叫做Fanout交换机,它采取广播模式,一个消息进来时,投递到与该交换机绑定的所有队列。
RabbitMQ支持消息的持久化,也就是数据写在磁盘上,为了数据安全考虑,我想大多数用户都会选择持久化。消息队列持久化包括3个部分:
(1)exchange持久化,在声明时指定durable => 1
(2)queue持久化,在声明时指定durable => 1
(3)消息持久化,在投递时指定delivery_mode => 2(1是非持久化)
如果exchange和queue都是持久化的,那么它们之间的binding也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。
(二)应用实际
我使用Linux服务器(ubuntu 9.10 64位),安装RabbitMQ非常方便。
先运行如下命令安装erlang:
apt-get install erlang-nox
再到rabbitmq.com上下载RabbitMQ的安装包,如下安装:
dpkg -i rabbitmq-server_2.6.1-1_all.deb
安装完后,使用
/etc/init.d/rabbitmq-server start|stop|restart
来启动、停止、重启rabbitmq。
在正式应用之前,我们先在RabbitMQ里创建一个vhost,加一个用户,并设置该用户的权限。
使用rabbitmqctl客户端工具,在根目录下创建”/pyhtest”这个vhost:
rabbitmqctl add_vhost /pyhtest
创建一个用户名”pyh”,设置密码”pyh1234″:
rabbitmqctl add_user pyh pyh1234
设置pyh用户对/pyhtest这个vhost拥有全部权限:
rabbitmqctl set_permissions -p /pyhtest pyh “.*” “.*” “.*”
后面三个”*”代表pyh用户拥有对/pyhtest的配置、写、读全部权限
设置好后,开始编程,我用Perl写一个消息投递程序(producer):
#!/usr/bin/perl
use strict;
use Net::RabbitMQ;
use UUID::Tiny;my $channel = 1000; # channel ID,可以随意指定,只要不冲突
my $queuename = “pyh_queue”; # 队列名
my $exchange = “pyh_exchange”; # 交换机名
my $routing_key = “test”; # routing keymy $mq = Net::RabbitMQ->new(); # 创建一个RabbitMQ对象
$mq->connect(“localhost”, { vhost => “/pyhtest”, user => “pyh”, password => “pyh1234″ }); # 建立连接
$mq->channel_open($channel); # 打开一个channel
$mq->exchange_declare($channel, $exchange, {durable => 1}); # 声明一个持久化的交换机
$mq->queue_declare($channel, $queuename, {durable => 1}); # 声明一个持久化的队列
$mq->queue_bind($channel, $queuename, $exchange, $routing_key); # 使用routing key在交换机和队列间建立绑定for (my $i=0;$i<10000000;$i++) { # 循环1000万次
my $string = create_UUID_as_string(UUID_V1); # 产生一条UUID作为消息主体
$mq->publish($channel, $routing_key, $string, { exchange => $exchange }, { delivery_mode => 2 }); # 将消息结合key以持久化模式投递到交换机
}$mq->disconnect(); # 断开连接
消息接受程序(consumer)大概如下:
#!/usr/bin/perl
use strict;
use Net::RabbitMQ;my $channel = 1001;
my $queuename = “pyh_queue”;
my $mq = Net::RabbitMQ->new();$mq->connect(“localhost”, { vhost=>”/pyhtest”, user => “pyh”, password => “pyh1234″ });
$mq->channel_open($channel);while (1) {
my $hashref = $mq->get($channel, $queuename);
last unless defined $hashref;
print $hashref->{message_count}, “: “, $hashref->{body},”\n”;
}$mq->disconnect();
consumer连接后只要指定队列就可获取到消息。
上述程序共投递1000万条消息,每条消息36字节(UUID),打开持久化,共耗时17分多钟(包括产生UUID的时间),每秒投递消息约9500条。测试机器是8G内存、8核志强CPU。
投递完后,在/var/lib/rabbitmq/mnesia/rabbit@${hostname}/msg_store_persistent目录,产生2G多的持久化消息数据。在运行consumer程序后,这些数据都会消失,因为消息已经被消费了。
http://www.ibm.com/developerworks/cn/aix/library/au-learning_erlang/index.html
如果您是 UNIX 开发人员或系统管理员,很可能会有自己喜欢的一套工具。您可能喜欢 Perl、Python、sed 和 awk 或 Ruby。也可能喜欢 Java™ 或 C++。当您熟悉了一套工具之后,可能不愿意再花时间学习新的语言。必须有强有力的理由证明这么做是值得的。学习 Erlang 就有足够强有力的理由。在本文中,我会解释为什么应该学习 Erlang 以及如何开始。
多核系统的时代已经到来了,但是我们还没有准备好。许多语言依赖于脆弱的并发机制,比如线程和共享的状态,甚至是更糟的全局锁(它导致一个线程只能在单一核上执行)。典型的 *nix 服务器有 24 个核。编程人员很容易编写出过于复杂且容易出错的线程化代码;也很容易编写出只使用 24 核系统上的一个内核的代码,无法充分利用整个 CPU。对于这个问题,一个解决方案是使用专门针对伸缩设计的函数语言。Erlang 没有可变的状态,这是它与 *nix 专业人员使用的大多数流行语言的重要差异。另外,并发是它的固有特性。实际上,其他许多语言都是围绕对象构建的,而 Erlang 是围绕进程构建的。因为由语言(而不是操作系统)控制并发机制,所以生成数千甚至数百万个进程是很普通的。在 Erlang 中,处理问题的方式不一样,这恰恰可以解决当今的计算难题。
喜欢命令行的人会很喜欢 Erlang 的交互式 shell。可以通过交互式 shell 输入表达式、编译代码以及与进程进行通信。它的设计相当优雅,让我觉得就像是在管理使用 Lights Out Management 卡的 *nix 系统和先进的虚拟化系统一样。假设您已经在系统上安装了 Erlang。如果还没有,则需要通过 参考资料 了解如何下载并安装它。安装 Erlang 之后,会在路径中找到 "erl"。输入 "erl" 时,会进入交互式提示,可以在这里输入命令。
lion% erl Erlang R14B (erts-5.8.1) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.1 (abort with ^G) 1> |
要想看到帮助菜单,则应该执行下面的 help
命令。注意,每个表达式必须用点号结束,告诉解释器可以执行该表达式了。另外,可以随时输入 "q().",参见下面的帮助菜单输出。
Eshell V5.8.1 (abort with ^G) 1> help(). ** shell internal commands ** b() -- display all variable bindings e(N) -- repeat the expression in query <N> f() -- forget all variable bindings f(X) -- forget the binding of variable X h() -- history history(N) -- set how many previous commands to keep results(N) -- set how many previous command results to keep catch_exception(B) -- how exceptions are handled v(N) -- use the value of query <N> rd(R,D) -- define a record rf() -- remove all record information rf(R) -- remove record information about R rl() -- display all record information rl(R) -- display record information about R rp(Term) -- display Term using the shell's record information rr(File) -- read record information from File (wildcards allowed) rr(F,R) -- read selected record information from file(s) rr(F,R,O) -- read selected record information with options ** commands in module c ** bt(Pid) -- stack backtrace for a process c(File) -- compile and load code in <File> cd(Dir) -- change working directory flush() -- flush any messages sent to the shell help() -- help info i() -- information about the system ni() -- information about the networked system i(X,Y,Z) -- information about pid <X,Y,Z> l(Module) -- load or reload module lc([File]) -- compile a list of Erlang modules ls() -- list files in the current directory ls(Dir) -- list files in directory <Dir> m() -- which modules are loaded m(Mod) -- information about module <Mod> memory() -- memory allocation information memory(T) -- memory allocation information of type <T> nc(File) -- compile and load code in <File> on all nodes nl(Module) -- load module on all nodes pid(X,Y,Z) -- convert X,Y,Z to a Pid pwd() -- print working directory q() -- quit - shorthand for init:stop() regs() -- information about registered processes nregs() -- information about all registered processes xm(M) -- cross reference check a module y(File) -- generate a Yecc parser ** commands in module i (interpreter interface) ** ih() -- print help for the i module true 2> |
输入这些命令并观察它们的作用,这对学习 Erlang 有很大的意义。在实际编写代码时,需要牢记的是,Erlang 中的状态是不可变的,所以在设置变量时会看到下面的输出,这可能让您很吃惊:
Eshell V5.8.1 (abort with ^G) 1> Var = 1. 1 2> Var = 2. ** exception error: no match of right hand side value 2 3> |
当然,在学习新语言时总是应该先研究一下 "Hello World" 示例:
4> io:format("Hello World~n"). Hello World ok |
在全面的 Erlang 课程中还要讨论很多东西,但这里最终只提供了一个常用的数据类型列表示例:
5> List = [1,2,3]. [1,2,3] 6> [Head|Tail] = List. [1,2,3] 7> Head. 1 |
在这个示例中,使用模式匹配提取列表的开头和结尾部分。模式匹配在 Erlang 中起到很大的作用,应该通过本文后面的 参考资料 进一步了解它。
用 Erlang 编写应用程序需要编写模块,而不只是编写交互式 shell 中看到的表达式。首先注意开发环境。如果打算进行很多 Erlang 开发工作,可以考虑使用带 Erlide 插件的 Eclipse 或 Emacs。在使用 Erlang 进行轻量级编程时,vim
表现得很好。
如果使用带 Erlide 的 Eunit 这样的 IDE,它会在编写模块代码时自动将代码编译为 beam 文件,这相当方便。也可以在 Erlang 交互式解释器中或通过 erlc
命令行编译器执行编译。下面是一个简单的模块,它将模块代码编译为 beam 文件:
%% This is a module that gets the operating system type -module(fingerprint). -export([get_os/0]). get_os ()-> os:cmd("uname"). |
要想以交互方式编译模块代码并运行它,则应该再次运行 erl
:
lion% erl Erlang R14B (erts-5.8.1) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.1 (abort with ^G) 1> c(fingerprint). {ok,fingerprint} 2> fingerprint: get_os/0 module_info/0 module_info/1 2> fingerprint:get_os(). "Darwin\n" 3> q(). ok |
如果在 shell 中直接列出文件,那么您会看到已经创建了 fingerprint.beam 文件。也可以通过运行以下命令创建这个 beam 文件:
lion% erlc fingerprint.erl |
如果您在编写 Erlang 代码,很可能是要构建高可用的系统。如果是这样,一定要从一开始就注意细节并为代码编写单元测试。对于前面的模块,相应的测试像下面这样:
%% This is a module that gets the operating system type -module(fingerprint_with_test). -include_lib("eunit/include/eunit.hrl"). -export([get_os/0]). get_os ()-> os:cmd("uname"). get_os_test ()-> get_os(). |
要点是,要想用 EUnit 测试模块,则需要包含头文件(如上所示)并按照 "_test" 模式命名函数。如果这样做,则会自动导出所有测试函数并通过下面的代码运行它们。
Eshell V5.8.1 (abort with ^G) 1> c(fingerprint_with_test). {ok,fingerprint_with_test} 2> fingerprint_with_test:test(). Test passed. ok 3> |
还应该注意的是,可以在脚本模式下运行 Erlang,这样就不需要先编译代码。为此,需要运行 R11B4 或更高版本的 Erlang 副本。我们可以略微修改前面的示例,通过添加脚本解释器行和主函数把它变成脚本:
#!/usr/local/bin/escript get_os ()-> os:cmd("uname"). main(_) -> io:format("OS Platform: ~p~n", [get_os()]). |
下面是输出:
lion% chmod +x fingerprint.escript lion% ./fingerprint.escript OS Platform: "Darwin\n" |
最后,与 Perl 或 Ruby 一样,还可以在命令行中编写单行脚本。可以将同一个示例编写为单行脚本,如下所示:
lion% erl -eval 'io:format("OS Platform: ~p~n", [os:cmd("uname")])' -noshell -s init stop OS Platform: "Darwin\n" |
分布式计算变得很容易:Mac OS X 和 Ubuntu 之间的远程消息传递
掌握了一些基础知识之后,我们就可以开始做真正有意思的事情了,比如构建相互通信的进程的集群。因为有 Location Transparency 特性,所以很容易在单一系统上测试集群,然后将它转移到大型分布式集群上,其中的系统可以位于世界各地!在下面的示例中,我在 Ubuntu(通过虚拟机)和 Mac OS X 上启动 Erlang,只用几行代码就可以远程地运行代码。注意,对于这个示例,假设已经正确地配置了本地 DNS 和防火墙设置(如果有的话)。
首先,在 Ubuntu 和 OS X 节点上都启动 Erlang:
mac% erl -name mac -setcookie test |
ubuntu% erl -name mac -setcookie test |
完成上述操作之后,就可以从 OS X 机器建立通信,通过调用 nodes 函数验证是否可以看到远程节点:
(mac@lion.local)1> net_adm:ping('ubuntu@ubuntu.localdomain'). pong (mac@lion.local)2> nodes(). ['ubuntu@ubuntu.localdomain'] |
现在,可以执行任意代码:
(mac@lion.local)15> rpc:call('ubuntu@ubuntu.localdomain', os, cmd, ['uname -a']). "Linux ubuntu.local 2.6.35-24-generic #42-Ubuntu SMP Thu Dec 2 01:41:57 UTC 2010 i686 GNU/Linux\n" |
很容易看出 Erlang 是多么强大,使用 Erlang 进行进程间通信是多么容易。尽管这是一个非常简单的示例,但您应该能够通过它体会到 Erlang 的强大之处。
本文讨论了使用 Erlang 的理论和实践,但是还有很多东西要学习。在今后几年,使用 Erlang 编写的大型生产系统会急剧增加。最近的一篇 Harvard Business Review 文章指出,“短缺” 是促进创新的首要因素。现在短缺的是在处理高度灵活且可伸缩的系统方面的经验丰富的专业人员。您现在应该努力成为 Erlang 语言专家,这会为您提供强大的竞争优势。
如果您有兴趣进一步了解 Erlang,我给出了以下建议。最好阅读一本或所有的 Erlang 书籍,有三本刚出版的书可供选择。这些书都不错。另外,您可以学习 tryerlang.org 上的交互式教程(请参阅 “参考资料”)。最后,还可以试试一些正在发展的 Erlang 项目,比如 CouchDB、RabbitMQ 和 Ejabbrd。
学习
- 进一步了解并安装 Erlang。
- 阅读博客 Scarcity is the number one key to innovation。
- Interactive Erlang tutorial
- 下载并安装 Erlang。
- lErlang FAQ
- Erlang and OTP in action
- Erlang Programming, O'Reilly
- Erlang programming rules and conventions
- Seven languages in seven weeks
- Erlang Cookbook
- Erlang Factory
- CEAN (Comprehensive Erlang Archive Network)
- EUnit
- “Erlang 编程简介,第 1 部分”(developerWorks,2011 年 7 月):Erlang 是一种多用途编程语言,主要用于开发并发和分布式系统。它最初是一种专有的编程语言,Ericsson 使用它来开发电话和通信应用程序。Erlang 在 1998 年开放了其源代码,之后,由于一些倍受瞩目的项目(比如 Facebook 聊天系统)和创新的开放源码项目(比如 CouchDB 的面向文档数据库管理系统)使用了 Erlang,Erlang 在近几年越来越流行了。在本文中,将了解 Erlang 的概况,并将它的函数编程风格与其他编程模型(比如命令式、过程式和面向对象编程)进行对比。首先,学习如何创建第一个程序(Fibonacci 递归函数)。然后,了解 Erlang 语言的基础知识,对于习惯使用 C、C++、Java 和 Python 的开发人员,一开始可能有点儿困难。
讨论
- 参与 developerWorks 博客 并加入 developerWorks 社区。
- 加入 My developerWorks 社区。
- 参与 AIX 和 UNIX® 论坛:

Noah Gift 是 O'Reilly 出版的 Python For UNIX and Linux System Administration 的作者之一,现在还在为 Manning 撰写 Google App Engine In Action 一书。他是一名作家、演说家、顾问和社区负责人,并为 Red Hat Magazine、O'Reilly 和 MacTech 撰稿。他的咨询公司的网站是 http://www.giftcs.com,在 http://noahgift.com 可以找到他的许多作品。还可以在 Twitter(http://twitter.com/noahgift)上关注他的近况。
他拥有加州洛杉矶的 CIS 的硕士学位,以及加州 Poly San Luis Obispo 的营养科学学士学位。他是通过 Apple 和 LPI 认证的系统管理员,曾经在许多公司工作过,如加利福尼亚理工学院、Disney Feature Animation、Sony Imageworks、Turner Studios 和 Weta Digital。在空闲的时候,他喜欢和妻子 Leah 以及他们的儿子 Liam 一起度过,谱写钢琴曲、参加马拉松比赛以及积极地参与体育活动。
http://erlang-china.org/misc/simplified_erlang.html
这是发表在《程序员》杂志第9期上的一篇简化版Erlang介绍文章。有人提起,于是就转发到这里来了。
Erlang 是程序界的幽灵船,它凭空出现,没人知道它从哪儿来,是怎么回事,也无人知晓它来做什么,要往何处去。
——江湖传言
相信对很多人来说,上面的说法颇为传神的捕捉到了围绕在 Erlang 周围的神秘气息。我们越来越多的见到这个名词,或者道听途说有关它的种种神奇。但每次试图一探究竟,总会在面对它那“怪异”的代码时感到气馁,而那些“不合情理”种种限制,则更加让人费解。很难想象,为什么有人会发明这么怪异的语言,而它为什么又会受到广泛的关注,更别提要如何用这么“别扭”的语言来编写程序了。本文试图为这些问题提供一些线索,但真正的答案其实一直深埋在你的心底,象往常一样,也要靠你自己的思考和努力才能获取。
从哪里来?
话说早在 1981 年(20 多年前了),瑞典电信巨头 Ericsson 就成立了 CSLab 实验室。其目标之一就是“为未来的软件系统提出一些新的架构(architecture)、概念(concepts)和
结构(structure)”,具体来说,也就是为“未来的电信设备”准备技术。到 1985 年 Joe Armstong 老爷爷( Erlang 的创始人之一)加入 CSLab 实验室,第二年,他开始着手进行以后被称作 Erlang 的技术的研究工作。这项工作最初的目标被设定为 Prolog 语言增加对并行进程的支持。到了 1987 年,一切进展顺利,雏形之中的 Erlang 被以 Prolog 扩展的形式实现了出来。而在这一年的晚些时候 Erlang 这个名称(来自于瑞典的数学家 Erlang,而不是所谓的 ERicsson-LANGuage )也第一次被人提出。此时的 Erlang 尚未成形,只有名字没有语法。
此后 Ericsson 的一个小组开始用 Erlang 来实现一个交换机原型,而 CSLab 的 Erlang 小组则忙于提高语言的性能。到了 1990 年,这些尝试终于有了成果:Erlang 的许多思想在原型项目中得到印证并被确定下来(这就是日后 Erlang 以及 OTP 的核心思想),而 Erlang 也有了自己的虚拟机(不再是 Prolog 的实现)和自己的语法(不再是 Prolog 的方言)。我们可以说,在这个时刻,作为一门计算机语言的 Erlang 正式诞生了。此时的 Ericsson 看到了 Erlang 的潜力,并于 1993 年成立 Erlang System AB 子公司来向合作伙伴们推广 Erlang,同年还发布了 Erlang 的第一个商用版本。
1995 年是 Erlang 的幸运年,知名的 AXD301 项目启动(它是迄今为止最为庞大的采用 Erlang 以及最为庞大的采用函数编程语言的商用项目),其目标是接替失败了的 AXE-N 项目,构造下一代的交换机。这是 Erlang 被发明之后的第一次大规模商用,此后 Erlang 团队一直围绕着这个多达 1.7M 行的庞大项目开展工作,整个过程一直持续到 1998 年 AXD301 项目成功才告一段落。这一阶段的主要成果是 OTP(Open Telecom Platform) 库的成熟。1998 年 Erlang 的第一次好运宣告结束。虽有 AXD301 项目的大获成功,但 Erlang 仍被 Ericsson 禁止在新的项目中使用。受到挫折的 Joe Armstrong 和 15 位原 Erlang 团队的同事黯然离开,成立了 Bluetail AB ,继续 Erlang 的事业(这个公司后来辗转被北电收购,而 Joe Armstrong 本人现在又回到了 Ericsson,中间的过程尚不为人知)。
Ericsson 放弃 Erlang 的原因至今都是众说纷纭,其中一个说法是“Ericsson 想成为一个软件技术的消费者而不是一个软件技术的生产者”,另一个说法则是“Ericsson 希望回归电信业界的标准”(即,回归C/C++),但这两个说法都流于太过冠冕堂皇,让人无法信服,整件事情始终透着一股“公司政治”的味道,事件的真正的原委我们只能猜测,或许只有当事人才能解释清楚。
从事后诸葛的角度来说,此次弃用事件,可谓影响深远。一方面,这当然是 Ericsson 的昏招(想想 Sun 和 Java),但何尝又不是程序界的幸事?若无此事,恐怕也不会有 1998 年底 Erlang 的开源,假若果真如此,那今天我们大概也无法得见作为 Ericsson 秘密武器的 Erlang 。而另一方面,此次事件也大大打击了 Erlang 团队的士气,使得 Erlang 的发展一度停滞,直到今天,事件的一些影响仍在持续(比如,在此之后发展出来的一些库和工具仍然散落在历史的秘辛之中,不为人知)。不过对于 Joe 本人而言,结果并不全是坏事,至少他所创立的 Bluetail AB 公司受到投资者追捧,历经多次收购,应是大有收获。
从 2001 年到 2004 年,业界遭遇 .COM 泡沫的崩溃(史称第一次互联网寒冬),受到波及的 Erlang 只能“苟延残喘”地过日子,好在项目和人才并没有随着泡沫的崩溃而流失。相反,挨过了冰河期的 Erlang 渐渐又恢复了活力。时间到了 2006 年, Erlang 实现了支持 SMP 架构的虚拟机,在多核冲击波来临之前,它已为此做好了准备。此后的事情就不再是掌故,而是当下的现实。当多核芯片和问题一起最终被摆上软件开发人员的桌面,对性能的焦虑,让“不走寻常路”的 Erlang 终于走到了聚光灯下。
这就是 Erlang 从过去一路跌跌撞撞走到现在的由来。
是怎么回事?
破除疑惑的不二法门就是——动手。更何况,安装并让 Erlang 跑起来,根本就不是什么难事。
Erlang 几乎可以运行于从手机到大型机的一切计算机系统。在 Erlang 的官方网站,能够下载到它的最新版本(源码或二进制发布版)。对于 Windows 用户而言,最为简便,下载回最新的安装包(.exe文件),运行,标准的安装步骤,一路 next 即可。基于 Debain 的 Linux 系统,也有二进制的发布版可以安装,在命令行输入以下命令就可以了。
$ apt-get install erlang
apt 安装的 Erlang 版本稍微有一些延迟,与之同样便利以及同样延迟的还有 Mac OS 下的 MacPorts 系统。若想使用时下最新的版本,可以采用源码编译的方式来进行安装。在任何平台下,都可以用这种方式来安装 Erlang 。一个典型的从源码安装的步骤是这样的:
$ tar -xzf otp_src_R11B-4.tar.gz
$ cd otp_src_R11B-4
$ ./configure
$ make
$ sudo make install
安装完毕即可启动 Erlang 的 Shell 开始感受,如果用过 ruby 或 python ,那么对于这样的交互式 Shell 你一定不会感到陌生。简单的 hello world 是这样的:
$ erl
Erlang (BEAM) emulator version 5.6.3 [source] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.6.3 (abort with ^G)
1> io:format(“~s~n”, ["Hello World!"]).
Hello World!
ok
2> q().
ok
* Erlang 中所有语句的执行都会产生结果,比如 io:format 函数是进行格式化输出,它的执行结果是 ok , q 函数是退出 Shell ,它的执行结果也是 ok 。
* ok 是 Erlang 中的值,它属于Atom (原子)类型,Atom 类型正如其名,乃是 Erlang 中“不能继续分割的最小粒子”,在概念上可以对应于常量或枚举类型。
准备随便逛逛的同学要注意了,因为此时的你马上就会碰到让你“水土不服”的“Erlang 几大怪”。
变量不能改
这条很简单,在 Erlang 中有规定:所有的变量都不可以二次赋值。是的,这么简单的操作也是不被允许的,比如:
1> X = 1.
1
2> X = 2.
** exception error: no match of right hand side value 2
* 上述的 X 是变量, Erlang 中的变量必须以大写字母开头。
* Erlang 中的变量是动态类型的,也就是说它可以“装下”任何类型的数据,因而也就无须类型声明。
所有第一次碰到这个规则的人,脑子里都会冒出无穷的疑问。反应大多类似于:“疯了,连变量的值都不能改!”或者“我晕,要怎么写程序?”。其实,这并不是 Erlang 的怪癖,很多其他的 FP (函数编程)语言都有相同的特性。而“变量不能改”也并不意味着“不能表达变化的状态”。 Erlang 会通过其他的方式来表达,其中的一种就是递归,确切的说,就用“新的状态”作为参数,让函数进入下一次的递归。
没有循环
在 Erlang 中,翻遍手册也找不到你所熟悉的 for 循环和 while 循环,不仅如此,根本也找不到任何形式的循环语句。我知道你在想:“没有循环结构的程序,也能算得上是程序?”。跟上面一样,“没有循环语句”也不意味着“不能实现循环结构”。Erlang 用递归来表达循环的逻辑,比如这样:
-module(ex1).
-export([fac/1]).
fac(1) -> 1;
fac(N) -> N * fac(N-1).
* Erlang 的程序构造单位是 module (对应 Java 中的 class)。
* Erlang 用 export 声明函数函数可以在 module 外部调用(对应 Java 中的 public,所不同的是只能公开函数)。
* fac/1 表示有一个参数的 fac 函数,也就是说,如果另外还有带两个参数的 fac 函数,可以用 fac/2 表示。基本上,这是两个完全独立的函数,只是恰好名字相同而已。
* fac/1 函数有两个子句(可以理解为,两个逻辑分支),子句之间用分号分隔,用句号结束整个函数。Erlang 有“模式匹配”,也就是说在调用 fac 时,它会检查参数,如果参数值为 1 则执行第一个子句,否则,会执行下一个子句。可以想象,若有 N 个子句,这样的参数检查和流程分支过程会一个子句一个子句的逐一进行。
将这个文件命名为 ex1.erl ,然后这样编译和运行这个程序:
$ erl
Erlang (BEAM) emulator version 5.6.3 [source] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.6.3 (abort with ^G)
1> c(ex1).
{ok,ex1}
2> ex1:fac(10).
3628800
* ext1:fac(10) 意味着使用参数 10 来调用 ext1 模块的 fac 函数()。
* c 是 Erlang Shell 默认引入的函数(也就是说,不用指定模块),用来编译源文件。
用这样的代码来计算阶乘,可谓极简。但“常识”告诉我们,递归太深常常意味着堆栈溢出。象上面这样的递归终会带来问题(比如用一个非常大的 N 来调用),在 Erlang 中也是一样。若要依赖递归来实现循环,上面的代码必须重构,变成这样:
-module(ex2).
-export([fac/1]).
fac(N) -> i_fac(1, N).
i_fac(Acc, 1) -> Acc;
i_fac(Acc, N) -> i_fac(N * Acc, N – 1).
将上面的代码保存为 ex2.erl 再来编译和运行:
$ erl
Erlang (BEAM) emulator version 5.6.3 [source] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.6.3 (abort with ^G)
1> c(ex2).
{ok,ex2}
2> ex2:fac(10).
3628800
我们看到,在不改动接口的前提下,重构实际上只是引入了一个名为 Acc 的新参数,它表示“上一次递归的运算结果”。但这个重构使得 i_fac 对于自身的调用(也就是递归)成为了“函数末尾的调用”,而且新的递归也不需依赖于堆栈之中的任何数据(因为上次计算的结果已经被重构成了函数的一个新参数)。满足这种条件的递归被称作“尾递归”,在编译层面,尾递归可以被优化成一个简单的跳转语句,因而,相比之前的递归,已经消除了堆栈溢出的风险。
现在回头看一下,没有变量,也没有循环语句,我们同样实现了阶乘的计算,你能说这不是一个程序么?也许你会认为“真正复杂的应用程序”,必然会有一些无法用上面这种方式来表达(或者至少是表达起来非常别扭)的“复杂业务逻辑”。我认为这是“很有项目经验”的想法,如果只有这些设施,确实不够用。我们看到 Erlang 从程序员手中拿走了许多东西(这些都是我们的心爱之物),基于常识判断,它必须要提供一些新东西,否则就会变成一个只有脑残人士才会使用的东西。没错,不知道你准备好了没有,但它确实提供了一大堆新东西。在这些新东西中,最为核心的概念就是进程。进程?没错,就是进程,甚至都不是听起来更加拉风的线程或者纤程。但,名虽一样实则不同,此进程非彼进程也。
一切皆是进程
习惯了 OOP 的“一切皆是对象”,猛然听到一个句式相同的“一切皆是进程”,难免会有一种相当复杂的感觉(怕怕)。而看到与 OOP 相对应的 COP (Concurrent Orient Programming 面向并发编程),如果觉得“李逵碰见了李鬼”,恐怕也不能算是大惊小怪。Erlang 是以进程为核心概念的系统,它的进程是自己实现的“超轻量级”进程,而非操作系统所提供的大家伙(据说它们也准备减肥了)。老实说,对于“超轻量级”这种“销售用语”大部分人都已经有了免疫,未免大家睡着,要来点感性认识振奋一下才好—— Erlang 的系统能轻松支持十万数量级的进程数目,这并不需要动用大型机,任何一台普通 PC 就够,退休在家的 486 都可以。这么说吧,在 Erlang 这样的 COP 系统里,我们可以象以往在 OOP 系统中使用对象一样,只管放心大胆的使用进程就对了。
回到“真正复杂的应用程序”,对于那些“复杂业务逻辑”,在 COP 中的做法其实也和 OOP 中的差别不大,无非是拆开了装装上了拆的那套老把式。吃透业务逻辑,将其分解为若干个进程(而不是对象),再把这些进程装起来,如果没出什么岔子就可以收工回家了。谈到组装,在 Erlang 里组装进程不用焊锡(或者螺丝),而是用消息。也就是说,一个进程就像是一个黑漆漆的集成块,只能看到那几个脚(接口),你永远也不知道那块黑塑料里面到底发生了什么,你所能做的就是把这些脚和其他的元件焊在一起(让它们能够发送和接收消息)。实际上,消息是进程之间通信(协作)的唯一方式。换句话说,进程之间并不共享任何东西,它们之间只能互相发送消息而已。说了一堆虚的,现在来点实际的,让我们来动手实现一个微小的“业务单位”——计数器。
-module(counter).
-export([start/0, stop/1]).
-export([inc/1, dec/1, reset/1, current/1]).
start() ->
spawn(fun() -> loop(0) end).
stop(Pid) ->
Pid ! stop.
inc(Pid) ->
Pid ! inc.
dec(Pid) ->
Pid ! dec.
reset(Pid) ->
Pid ! reset.
current(Pid) ->
Pid ! {current, self()},
receive
Any -> Any
end.
loop(Count) ->
receive
stop -> ok;
inc -> loop(Count + 1);
dec -> loop(Count – 1);
reset -> loop(0);
{current, Pid} ->
Pid ! Count,
loop(Count)
end.
* spawn 就是启动一个新的进程,新进程执行其中定义的匿名函数(执行完所有的流程,新进程会自己退出),新进程的标识作为 spawn 的结果被存在变量 Pid 之中。
* fun() -> loop(0) end 定义了一个匿名函数,这个函数的唯一任务就是用参数 0 来调用 loop 函数。你最好习惯这种语法,因为这在任何一个 FP 之中都是司空见惯的东西(包括 JavaScript)。
* Pid ! stop 的意思是向 Pid 所标识的进程发送消息,消息的内容是 Atom 值 stop。在 Erlang 中,只要你知道一个进程的标识就可以向它发送消息。你会看到 counter 的各个“业务接口”其实就是向进程发送各种各样的消息。
* receive 的意思是接收消息。每一个进程都有自己的消息队列。如果消息队列为空, receive 语句会等待消息。而如果收到了消息,那么和函数的子句一样,它会对接收到的消息逐一进行“模式匹配”(在 Erlang 中模式匹配无处不在,是一种更为便捷的分支结构),如能匹配,则将这个消息从队列中删掉,并执行其后的流程;如果无法匹配任何模式,那么这条消息就不会被处理(没准会在执行到另外一个接收语句的时候能够匹配得上呢),继续对下一条消息从头进行匹配。象 Any -> 这样的模式能够匹配任何消息(别忘了,Erlang 是动态类型语言,它的变量能装下任何类型的数据)。
* 在 loop 的模式中,如果匹配了 stop ,则 loop 函数会返回 ok ,除此之外的其他模式,都是在用新的 Count 值继续递归调用自己,也就是说并未返回。对于由 spawn 语句生成的新进程来说,如果 loop 返回,也就意味着它执行完了所有的流程,会自行退出。
将上面的代码保存为 counter.erl 编译运行之:
$ erl
Erlang (BEAM) emulator version 5.6.3 [source] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.6.3 (abort with ^G)
1> c(counter).
{ok, counter}
2> Pid = counter:start().
<0.32.0>
3> counter:inc(Pid).
inc
4> counter:inc(Pid).
inc
5> counter:current(Pid).
2
6> counter:reset(Pid).
reset
7> counter:current(Pid).
0
8> counter:stop(Pid).
stop
运转良好!COP 和 OOP 的版本,在实现方式上大有不同,但对外的接口却几乎一样。实际上,上述代码之中的 loop 就是其中的一种“状态变化”机制。它的参数 Count (也就是当前的计数值)在每次递归调用时都会“更改”,并在这些递归调用之中得以“传递”。如果我们将视角拉开一点,可以把这个 counter 看作是“复杂应用程序”当中的一个部件,你可以想象,还会有若干个完成其他各种功能的部件,它们彼此就是这样,通过“消息”来组装。这种组合方式,可以构造任何复杂程度的系统。
为什么要这么做?
上面我们看到 Erlang “绕了一个大圈子”来实现“不过是相同的功能”,而且相比 OOP 的实现,似乎更为繁琐。那么,这么做的目的又是什么呢?要回答这个问题,我们不妨看看一段简单的代码(Java 代码):
…
public void some_operate(int x){
this.value = something_slow(this.value, x);
}
…
假设在某一个时刻,线程 1 进入这个函数准备计算 something_slow,而与此同时,线程 2 刚刚算完 something_slow,准备更新 this.value 的值。那么,在线程 1 执行完 something_slow 时, 此前线程 2 的计算结果就会被覆盖。这是标准的“线程互斥”,解决方法也很“标准”,那就是加锁(枷锁?):
…
public void synchronized some_operate(int x){
this.value = something_slow(this.value, x);
}
…
加锁的要义就是——好东西大家共享,但要排队,只能一个个的来,否则就会乱了套了。暂且不谈多个锁会带来多么复杂的互锁和死锁,将会累死多少脑细胞,单就效率而言“一个线程在做操作,其他的就都得等着”,尤其是在多核 CPU 泛滥的时代,这种“高档硬件当低档硬件使”的做法多少有点说不过去。好吧,我承认,有的“业务逻辑”本身就是顺序排队的,但那是在“业务层面”的(用一个进程来作排队不就结了),这并不意味着每一行代码也都必须遵循这种毫无必要的限制。现在让我们回到锁的源头,问问自己为什么需要锁?因为我们允许“共享和更改”某个东西。如果不允许共享,也不允许更改,那么锁岂不是也就失去了存在的必要么?现在再来看看之前 Erlang 让我们水土不服的限制:“变量不能更改”以及“进程之间并不共享任何东西”。悟了吧(没悟的去找个榔头再敲一下)。绕这么大的圈子,不就是为了绕开锁么。
用来做什么?
绕开了锁,请出了进程。费了牛鼻子的劲整出来的 Erlang 究竟可以做点什么呢?看好了:
- 将一个任务的各个环节(针对流程切分)分给不同的进程执行,各干各的都不耽搁,此之为并行。
- 将一个任务的各个环节(将流程切分到多台机器上)分给不同机器上的进程执行,当负载增加时,只需加入新的机器即可满足需要,此之为分布。
- 将某个海量数据的处理任务(针对数据切分)分给多个进程执行,每个进程处理一部分,进程越多效率越高,此之为并发。
- 将某个海量数据的处理任务(将数据切分到多台机器上)分给不同机器上的进程执行,在其中的一部分失效时,整个服务不受影响,此之为容错。
- 上述由多个进程组成的系统,在多核系统上运行,在 CPU 数量增加时,程序执行的性能也同时自动获得增长,此之为多核加速。
是的,进程为所有这些提供了统一的概念体系,用 COP 的方式来思考,将能更容易的达成这些目标。
要往何处去?
“一切皆是进程”是 Erlang 的核心概念,可以说,掌握了进程这把钥匙,理解其他的 Erlang 概念会变得非常容易。而如果说能将 Erlang 比作是一场精彩的电影,那么,毫无疑问,更多的剧透只会带来更少的观影的乐趣,更多更精彩的 Erlang 世界,还是留待你自己去探索。
此外,还请记着“所有的计算机技术都只是工具,帮助你更好地达成目标的工具”。Erlang 也只是工具,而没有灵魂,就算它是绝世神兵,离开了主人也不过只是一块没有生命的死物。所以,象“你和我”这样的使用者才是真正的关键。“你的 Erlang ”要往何处去?乃至“整个社群的 Erlang ”要往何处去?这就要看“你”的了。
update: 更正了文中对 fac 函数功能描述的笔误(为阶乘,而非菲波拉契数列),谢谢指出错误的朋友。
btw. Google Docs 的 publish 功能竟然能够顺手就发到 WordPress 里来,太方便了。
http://svn.liancheng.info/cpie-cn/trunk/.build/html/introduction.html
Erlang是一门被设计用于编写并发、实时、分布式系统的新语言。
很多年来,并发实时系统的编程技术一直落后于串行应用的编程。当使用C或Pascal进行串行编程已经成为实践标准时,大多数实时系统的程序员还在倒腾着汇编。如今的实时系统可以使用Ada、Modula2、Occam等为并发编程提供了显式支持的语言来编写,或是仍旧使用C这样缺乏并发结构的语言。
我们对并发的兴趣源自于对一些展现出大规模并发的问题的研究。这是实时控制系统的一个典型属性。Erlang程序员可以显式指定哪些活动需要由并发进程来展现。这种观念与Occam、CSP、Concurrent Pascal等语言类似,但与那些并不为了对现实世界的并发进行建模而引入并发的语言不同,这些语言引入并发的目的只是为了将编译出可在并行处理器上运行的程序以获得更高的性能。
现今Prolog和ML等语言已经被大范围用于工业应用之中,并极大地降低了设计、实现和维护应用的成本。我们对Erlang的设计和实现也是为了将并发实时系统编程提高到一个相似的高度。
申明式语法
Erlang具备申明式的语法,且大部分都无副作用。
http://zh.wikipedia.org/wiki/Erlang
Erlang是一种通用的并行程序设计语言,它由瑞典电信设备制造商爱立信所辖的计算机科学研究室开发,目的是创造一种可以应付大规模开发活动的程序设计语言和运行环境。Erlang问世于1987年,经过十年的发展,于1998年发表开放源代码版本。Erlang是运作于虚拟机的解释型语言,但是现在也包含有乌普萨拉大学高性能Erlang计划(HiPE)[1]开发的原生代码编译器,自R11B-4版本开始,Erlang也支持脚本方式执行。在程序设计范型上,Erlang属于多重范型编程语言,涵盖函数式、并行及分布式。循序运行的Erlang是一个及早求值, 单次赋值和动态类型的函数式编程语言。
http://www.oschina.net/news/35143/postgre-proposed-new-name
PostgreSQL 网站上的 Wiki 显示 PostgreSQL 有计划将产品名称从 PostgreSQL 更改为 Postgres。
http://www.oschina.net/p/postgresql
PostgreSQL (也叫 Postgres)是一个自由的对象-关系数据库服务器(数据库管理系统),它在灵活的 BSD-风格许可证下发行。它提供了相对其他开放源代码数据库系统(比如 MySQL 和 Firebird),和对专有系统比如 Oracle、Sybase、IBM 的DB2 和 Microsoft SQL Server的一种选择。
PostgreSQL 不寻常的名字导致一些读者停下来尝试拼读它,特别是那些把SQL拼读为"sequel"的人。PostgreSQL 开发者把它拼读为 "post-gress-Q-L"。(Audio sample, 5.6k MP3)。它也经常被简略念为 "postgres"。
http://yk1001.blog.163.com/blog/static/18247692008517115116338/
Erlang概述
Erlang不但是一种编程语言,而且它具有比编程语言更加贴近操作系统的一些特性:并发线程、作业调度、内存管理、分布式、网络化等。据说使用Erlang编写的Yaws Web服务器,其并发性能是apache的15倍!
这个Erlang初始开源版本包含了Erlang的实现,同时它也是用于构建分布式高可用性系统的Ericsson中间件的最大组成部分。
Erlang具有以下特性:
并发性 - Erlang具有超强的轻量级进程,这种进程对内存的需求是动态变化的,并且它没有共享内存和通过异步消息传送的通讯。Erlang支持超大量级的并发线程,并且不需要操作系统具有并发机制。
分布式 - Erlang被设计用于运行在分布式环境下。一个Erlang虚拟机被成为Erlang节点。一个分布式Erlang系统是多个Erlang节点组成的网 络(通常每个处理器被作为一个节点)。一个Erlang节点能够创建运行在其它节点上的并行线程,而其它节点可以使用其它操作系统。线程依赖不同节点之间 的通讯,这完全和它依赖于单一节点一样。
健壮性 - Erlang具有多种基本的错误检测能力,它们能够用于构建容错系统。例如,进程能够监控其它进程的状态和活动,甚至这些进程是在其它节点上执行。在分布式系统中的线程能够配置为在其它节点故障的情况下自动进行故障恢复,并在故障节点恢复时自动迁移回到恢复节点。
软实时性 - Erlang支持可编程的“软”实时系统,这种系统需要反应时间在毫秒级。而在这种系统中,长时间的垃圾收集(garbage collection)延迟是无法接受的,因此Erlang使用了递增式垃圾收集技术。
热代码升级 - 一些系统不能够由于软件维护而停止运行。Erlang允许程序代码在运行系统中被修改。旧代码能被逐步淘汰而后被新代码替换。在此过渡期间,新旧代码是共存的。这也使得安装bug补丁、在运行系统上升级而不干扰系统操作成为了可能。
递增式代码装载 - 用户能够控制代码如何被装载的细节。在嵌入式系统中,所有代码通常是在启动时就被完全装载。而在开发系统中,代码是按需装载的,甚至在系统运行时被装载的。如果测试到了未覆盖的bug,那么只有具有bug的代码需要被替换。
外部接口 - Erlang进程与外部世界之间的通讯使用和在Erlang进程之间相同的消息传送机制。这种机制被用于和操作系统通讯、与其它语言编写的程序交互。如果出于高效率的需要,这种机制的一个特殊版本也允许例如C程序这样的代码直接链接到Erlang运行时系统中来。
Erlang组件
Erlang具有许多单独的组件,它们能够在开发应用时作为组建块(building blocks)使用。同时这些组件也熟知Erlang的系统消息(load、unload、start、stop、restart、change code)。
Inets - HTTP 1.0服务器和FTP客户端。
Mnesia - 使用Erlang的分布式实时数据库。它支持RAM复制、磁盘存储、动态改变shema、保存任意复杂的数据结构。Mnesia之所以非常快速,是因为它 运行在和应用相同的地址空间(因为Mnesia和应用都使用Erlang编写)。Mnesia展示了Erlang的强大:你能够使用多少种语言使用少于 20000行的代码编写一个全特性、工业强度、分布式的DBMS?
Orber - CORBA v2.0对象请求代理(ORB)。
SNMP – 可扩展的SNMP v1/v2代理和MIB编译器。
Erlang工具和代码库
Erlang具有一套常用工具库:
Appmon - 进程组图形监控(在本地和远程节点上)。
ASN.1 - 支持ASN.1基本标记法和BER、DER、PER编码规则的编译时和运行时的代码包。
Compiler - Erlang编译器。
Debugger - 图形化Erlang调试器。
ERTS - Erlang运行时系统,包括虚拟机、垃圾收集、端口映射守护进程。
GS - 编写图形用户接口的代码库。
IC - 把OMG的接口定义语言(IDL)转换到Erlang、C和Java语言的编译器。
Kernel - 运行Erlang系统所必须的C代码:Erlang内建功能(BIFs);代码、启动、命名服务;对网络和分布式的支持;装载器、连接器、记录器;操作系统和文件系统接口。
Mnemosyne - 可选的用于Mnesia的查询语言。
Mnesia Session - 以IDL定义的与Mnesia接口的外部语言,它们通过IIOP和erl_interface协议对Mnesia进行访问。
OS monitor (OS_MON) - 监控CPU、硬盘、内存使用情况,包括SNMPv1/v2 MIBs。并且提供了与Solaris syslogd、Windows NT事件日志的接口。
Parse tools - 用于Erlang的LALR-1解析生成器(yecc),它和yacc类似。Yecc使用BMF语法定义作为输入,生成Erlang代码作为解析输出。Yecc被用于生成Erlang解析器。
PMan - 跟踪、查看Erlang进程状态(在本地或者远程节点上)的工具。
SASL - 进程、错误、崩溃报告处理、报告浏览、释放处理、重载管理。
Stdlib - 标准代码库:输入、输出;基于内存、磁盘的表存储(ETS和DETS);图表、字典、列表、字符串、集合、队列;正则表达式;数学公式。Erlang解释 器、tokenizer、解析器、lint和格式化打印。用于容错服务器的通用框架、事件处理器、状态机和线程监管等等。
Table visualizer - 查看ETS和Mnesia表格的工具。
Toolbar - 简化了对Erlang工具的访问。
Tools - 覆盖分析器、优化器、基于文字的跟踪器、Emacs模式、Emacs TAGS文件生成器、make工具、调用图形化工具。
十分钟Erlang快速入门
启动Erlang
如果你使用unix系统的话,请输入“erl”;而如果你使用Window系统的话,请点击Erlang的开始图标来启动Erlang。你应该看到如下界面:
os prompt > erl
Erlang (JAM) emulator version 4.7.3.3
Eshell V4.7.3.3 (abort with ^G)
1>
“>”提示表示了系统正在等待输入。
将Erlang用作计算器
1> 2131836812671*12937192739173917823.
27579983733990928813319999135233
2>
记住每个表达式都是以句点和空格为结束符的!
编辑从前的表达式
我们使用简单的emacs行编辑命令编辑从前的表达式,最常用的命令如下:
?^P 获取上一行。
?^N 获取下一行。
?^A 将输入焦点移动到当前行首。
?^E 将输入焦点移动到当前行尾首。
?^D 删除当前光标所在的字符。
?^F 向前移动一个字符。
?^B 向后移动一个字符。
?回车 执行当前命令。
请注意: ^X 表示Control + X 。尝试按下Control+P,看看什么会发生?
编译你的第一个程序
在你所喜欢的文本编辑器中输入以下内容,并将其保存到文件中:
-module(test).
-export([fac/1]).
fac(0) -> 1;
fac(N) -> N * fac(N-1).
保存文件名为test.erl,请注意文件名必须和模块名相同。
通过输入c(test)编译程序,然后运行它:
3> c(test).
{ok,test}
30> test:fac(20).
2432902008176640000
4> test:fac(40).
815915283247897734345611269596115894272000000000
32>
Ok,现在你也可以尝试编写一些有趣的程序了。