Erlang 开发及应用[email protected]
What is Erlang?• ERicsson LANGuage?• 函数式编程语言 (FP)• 面向并发 (OC), 基于消息• Ericsson 创建 , 最初用于电信系统开发• 成熟 , 稳定 , 具有 20 多年历史• 适于电信系统 , 分布式系统 , 高并发服务器 • Open Source, 跨平台 , GC 不适于底层系统开发
History• 1980s Ericsson 实验室思考如何轻松开发电信系统应用• 1987 年左右 , Erlang 浮出水面• 1989 年 JAM 虚拟机 C 语言实现 • 1996 年 OTP 项目启动 , 融合开发经验 , 提供易用 , 强大的 Erlang 开发库 • 1998 年开源• 2007 年《 Programming Erlang 》出版• 目前版本 Erlang R13B1 (5.7.2)
Erlang 优势• 多核 SMP 支持• 内建分布式支持• 基于轻量进程及消息的高并发模型• 代码热替换• 开发速度快 , 高性能 , 高稳定性• FP 编程 , 代码灵活高效 , 副作用小• 丰富的分析及监控程序• 经过商业产品 , 长久大规模验证• OpenSource, 代码面前无秘密
Erlang Hello World代码 hello.erl: 1 -module(hello).2 -compile([export_all]).34 main() ->5 io:format("hello world!~n").编译 : $ erlc hello.erl运行 : $ erlEshell V5.7.1 (abort with ^G)1> hello:main().hello world!ok
Erlang Hello World CON'T1 -module(hello).声明模块名称 , 其必须和文件名一致 . 模块是 Erlang 项目中代码组织的基本方式 .2 -compile([export_all]).指明编译选项 ,export_all 用来导出所有本模块中的函数 ,exported function 是模块的接口 , 其他模块只能调用 exported function4 main() ->为函数头 (head), 包含函数名称和参数 , 后紧随一个 '->' 分割符5 io:format("hello world!~n").为函数体 (body), 包含 Erlang 表达式 , 这里调用 io 模块的 format函数在默认输出中打印 "hello world!"
在上面的运行结果中 , 最后有一个 "ok", 这是 io:format/1 的返回值 , 表示打印成功 ,Erlang 中任何函数都有返回值 .
Erlang 语法• Data Types
8 种基本类型 integer - 4, -4, 2#100, 16#4, 920828990801238101010.. float - 3.0, 3.5e2, 6.5e-2, (IEEE754 64bit) atom - hello, your_name, root@host, 'IsAtom' binary - <<"some text">> reference - make_ref(), 一个随机值 fun - fun() -> some_expr end port - 与外部应用进行交互的接口 pid - process identifier, 用来操作 process
2 种复合类型 tuple - {foo, male, 28, china, <<"i love erlang">>} list - [{ip, any}, {port, 1234}, binary]
Erlang 语法 CON'T• Pattern Match 语言层级的模式匹配 , 代码更加简洁 . 适用于函数调用 , case, receive, try 表达式及 "=" 操作
case Value of N when is_integer(N) -> N; _ when is_list(Value) -> list_to_integer(Value) end• 变量 大写字母或 "_" 开头 , 只能包含数字 , 字母 ,"_", "@". 如 X, Name1, _Phone, _, Host@ 变量分为 Unbound 和 Bound, Unbound 变量只能用在模式匹配中 . 变量 Bound 后 ,Value 就不可修改 变量只能单次赋值 ( 并发及调试考虑 ) N = 3 (ok) N = 4 ( oops! not match)
Erlang 语法 CON'T• Binary 匹配 使用 binary 可以轻松的实现二进制协议 . (1) 解析 IP 包 : -define(IP_VERSION, 4). -define(IP_MIN_HDR_LEN, 5)....
DgramSize = size(Dgram),case Dgram of<<?IP_VERSION:4, HLen:4, SrvcType:8, TotLen:16,ID:16, Flgs:3, FragOff:13,TTL:8, Proto:8, HdrChkSum:16,SrcIP:32,DestIP:32, RestDgram/binary>> when HLen >= 5, 4*HLen =< DgramSize ->OptsLen = 4*(HLen - ?IP_MIN_HDR_LEN),<<Opts:OptsLen/binary,Data/binary>> = RestDgram,
Erlang 语法 CON'T (2) 自定义协议 假如我们定义了一个协议 , 前 2 bytes(16 位 ) 标记消息体的长度 , 后面为消息体 , 最后为占用 1 个 byte 的结尾符 0xef, 示意图如下 : [--- length ---][--------- payload ------][ef] |------ 2 -------|---------- Length -------|-1-| ( 单位 byte)
则对应的 binary 匹配表达式如下 : ... Packet = ... case Packet of <<Len:16, PayLoad:Len/binary, 16#ef>> -> {body, PayLoad}; _ -> {error, invalid_packet} end.
Erlang 语法 CON'T• 序列化与反序列化 Erlang 中序列化非常简单 term_to_binary/1 - 将任意数据转化为二进制序列 binary_to_term/1 - 将编码的二进制数据转化为 Erlang 数据 比如 : Obj = {apple, {price, 2.0}, {origin, shandong}}, Bin = term_to_binary(Obj), Obj = binary_to_term(Bin)
CouchDB 中大量使用 erlang 的序列化相关函数,完成数据的存储与加载 .
Erlang 语法 CON'T• 函数 一切皆函数 , 每个调用都有 return, 函数可以作为基本数 据类型 . 作为返回值 : op_fun('+') -> fun(N1, N2) -> N1 + N2 end; op_fun('-') -> fun(N1, N2) -> N1 - N2 end. 作为参数 : > FIsEven = fun(E) -> E band 2#1 =:= 0 end. > lists:filter(FIsEven, [1, 2, 3, 4, 5, 6]). > [2, 4, 6]
Erlang 语法 CON'T• Tail Recursion 尾递归 Erlang 中没有 for, while关键字 可以利用递归实现循环 在 server 开发中 ,确保使用尾递归 : server_loop(Args) -> ...some action... server_loop(Args). 使用尾递归 , 可以消耗很少的内存 ,仅仅是一个地址跳转 . server_loop(Args) -> ...some action... server_loop(Args), other_fun().
Erlang 语法 CON'T• 发送Message "!" 基于消息通信 ,No Lock! No Shared Memroy!
Pid ! {msg, "hello, I love erlang"} 向 Pid( 本地或远程主机 ) 代表的进程发送消息• receive
1, 阻塞等待任意消息 :receive Msg -> okend3,等待消息 ,超时为 5 sec:receive Msg -> okafter 5000 -> timeoutend
2, 实现 sleep:receiveafter Time -> okend4,检测是否存在消息 :receive SomeMsg -> existafter 0 -> no_existend
Erlang 并发关于 Process• 每个 Process拥有一个 mailbox,保存消息 • Processes之间通过发送异步Message 进行交互 , 无共享状态• 轻量 ,兼有 OS Process 的隔离及 OS Thread 的高效• Process 具有自己 Stack, Heap, GC• Process 可以位于 Local,也可以位于 Remote Machine• Process 能够进行多种形式的管理及控制 (link, monitor,
exit signal)• Process 为 erlang 高并发 , 高容错 , 分布式的基础• 并发 Process 数 : default 32768, max 268435456
Erlang 并发 CON'T
• 创建 Process spawn(Fun), 比如 > spawn(fun() -> io:format("i'm ~p~n", [self()]) end). i'm <0.33.0>
<0.39.0> spawn(Mod, Fun, Args),比如 > spawn(io, format, ["i'm ~p~n", [self()]]).
i'm <0.33.0><0.41.0>
spawn_link, spawn_opt, spawn_monitor ...• 销毁 Process 进程内部调用 exit(Reason), 比如 exit(normal), 正常退出 进程内部发生异常导致程序退出 其他进程调用 exit(PidBeTerminate, Reason)
Erlang 并发 CON'T基于 Process 的 http server 框架(one loop process, per conection per process): setup up listen socket,spawn(listen_process).in listen_process: while can accept new client connect accept , spawn(client_process) loop
in client_process: process protocol, close socket.
Erlang 并发 CON'T使用 Erlang 我们可以 :以清晰的风格开发高并发的应用 我们将不在受困于 :线程池的复杂死锁 ,竞赛的窘迫内存泄露局部问题 , 导致的全局崩溃 与跨平台多核 SMP 的格格不入
Erlang 分布式• Erlang Node 是分布式通讯的基本单元 , 可以位于同一机器 or 多台机器 , 实现了原语级的节点通讯• Erlang Node 通过 erl -sname Name or erlang -name Name启动 , 同一台机器可以启动多个 Node• 每台机器上启动 Erlang Node时 , 都会启动一个
epmd(Erlang Port Mapper Daemon, port 4396), 用来进行Node 和 Machine之间的映射
• 不同机器的 Node之间通过 Tcp连接进行 Message传输( 可以自定义分布式通讯实现 , 如通过 ssh)
• global维护一个全局的 Nodes网络• spawn[_link|_opt] 都具有分布式版本 , 可以再其他节点创建 Process
Erlang 分布式 CON'T
• rpc 模块可以在其他 Node 上执行操作• slave, remsh, remote shell等方式启动 ,连接 Erlang
Node• Erlang 中进程具有位置透明性• 通过 message 及 receive 表达式 , 轻松实现同步 or异步 , timeout等网络通信中多种机制• Erlang 本身提供 tcp,udp等常规的网络编程方式• 使用 Erlang 内建分布式机制 , 可以快速开发多种应用 ,也可以基于 socket 开发各种专有应用
Erlang 分布式 CON'T
Erlang 分布式 CON'T
节点 A2连接节点 B2步骤1. Node A2, B2 启动 ,绑定一个本机端口 , 并注册到本机的
epmd(default port 4396)– A2连接 HostB epmd,请求获取 B2节点的绑定端口– HostB epmd将 B2 的 bind port 及 dist 协议版本等信息返回给 A2– A2 与 B2 协商 , 建立 tcp连接 , 如果连接成功 ,维护一个
tick, 来定期检测 B2节点 – A2 与 B2节点之间的消息 ,通过此连接进行发送
Erlang OTP
• OTP(Open Telecom Platform),其定义了一系列项目开发中需要的模式及部署升级策略,为提高开发效率,构建高效,稳定系统提供了巨大的帮助。• 同最初时的专有电信平台应用已没有太多关系• 当前系统都是采用 OTP 进行开发• Erlang 中各种 lib 都是基于 OTP 开发• 可以理解成某种轻量的框架,或者具体化的设计模式• behaviours 包含 :application, supervisor, gen_server,
gen_fsm, gen_event• application, release, release handling• 提供应用的部署,升级,回退等实现
Erlang OTP CON'TBehaviours通过定义一些简单的 callback 模块实现特定功能 .
• application - 定义 application,实现某种功能,由其他behaviours 组成
• supervisor - 定义一个 supervisor tree,实现各种策略的任务重启机制• gen_server - 定义一个通用的 server 模型,一个 process
loop,提供同步异步接口• gen_fsm - 实现一个状态机• gen_event - 实现一个 event manager 及 event handler 模型
与其它语言的交互• External App 外部应用崩溃不会影响 Erlang 虚拟机
o Ports - 通过 port 与外部应用交互 (stdin/stdout)o Erl_Iterface - 提供 c 的封装,方便开发 port 应用
• Link in Driver shared library (SO in Unix, DLL in Windows),影响 Erlang虚拟机稳定性(不推荐)• Port dirvers - 提供 c封装,运行在 erlang 虚拟机内部• C Nodes 遵照 erlang 的交互协议, 使用 c 实现的一个 erlang node• Jinterface 提供一系列与 Erlang 进行交互的 Java 包
Erlang 代码片段• 求某个数的阶乘
factorial(0) -> 1; factorial(N) -> N * factorial(N-1).
• 获取远程机器的 issue 信息( linux) -module(issue). -compile([export_all]). %% start server server() -> register(issue_server, spawn(fun server_loop/0)).
server_loop() -> receive {From, {get, issue}} -> From ! {issue, get_issue()}; _ -> ok end, server_loop().
Erlang 代码片段 CON'tget_issue() -> {ok, Bin} = file:read_file("/etc/issue"), Bin.
%% start clientclient(ServerNode) -> true = net_kernel:connect_node(ServerNode), {issue_server, ServerNode} ! {self(), {get, issue}}, receive {issue, Issue} -> io:format("server issue:~s~n", [Issue]) after 1000 -> io:format("receive issue time out~n") end.
调用:$ erl -sname server(server@litao)1> issue:server().true$ erl -sname client(client@litao)1> issue:client('server@litao').server issue:Ubuntu 9.04 \n \l
产品开发流程1.架构设计2.单台 or 分布式? Master-Slave or Grid? Monitor, Failover,
Net Comunication, Database, Replica ...3. OTP Behaviour
how many application?use supervisor, gen_server, gen_fsm, gen_event
4. Coding, 及单元测试用例 . 每个 module 都经过测试 (eunit)5.编写系统测试框架,覆盖测试,确保系统正确 (common test)6.压力测试,分析性能瓶颈,进行优化 (fprof)7.系统上线,监控功能 (ganglia, nagios, monit)8.新的功能或需求,重复 2-7
一些工具appmon - OTP application 监控工具cover - erlang 代码覆盖测试ntop - 显示 Node 中进程信息 (unix top)make - erlang 中的 make工具pman - erlang 中进程管理器tv - ets 和 mnesia 查看器fprof - erlang 系统性能分析common_test - erlang测试框架dialyzer - 代码静态分析debugger - 单步调试工具,基于 (tcl/tk)
学习资源Erlang官方网站http://www.erlang.org/doc Erlang China http://www.erlang-china.org/Erlang Mailisthttp://www.nabble.com/Erlang-Questions-f14096.htmlErlang Planethttp://www.planeterlang.org/Erlang 非业余研究http://mryufeng.javaeye.com/Erlang Display http://erlangdisplay.javaeye.com/
开源项目ejabberd - the Erlang Jabber/XMPP daemon RabbitMQ - AMQP serverCouchDB - schema-free document databaseTsung - multi-protocol distributed load testing Scalaris - distributed key-value storeDisco - MapReduce Framework Mochiweb - Powerful Http Server Tookit
自己动手发起 erlang 开源项目 !
案例• AXD301 • 高并发的电信交换机• 99.9999999% 可靠性 (~3ms 故障 / 年 )• 超过 100万行 Erlang 代码• 软实时系统 • 高容错
案例
• WEB IM 后台 (mochiweb)• 7+ 百万活跃用户• ~ 100 server• ajax + comet(long-polling)
更多应用