340 likes | 655 Views
Erlang开发及应用. litaocheng@gmail.com. What is Erlang?. ER icsson LANG uage? 函数式编程语言(FP) 面向并发(OC),基于消息 Ericsson创建, 最初用于电信系统开发 成熟, 稳定, 具有20多年历史 适于电信系统, 分布式系统, 高并发服务器 Open Source, 跨平台, GC 不适于底层系统开发. History. 1980s Ericsson实验室思考如何轻松开发电信系统应用 1987年左右, Erlang浮出水面 1989年JAM虚拟机C语言实现
E N D
Erlang开发及应用 litaocheng@gmail.com
What is Erlang? • ERicssonLANGuage? • 函数式编程语言(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 运行: $ erl Eshell V5.7.1 (abort with ^G)1> hello:main().hello world!ok
Erlang Hello World CON'T 1 -module(hello). 声明模块名称,其必须和文件名一致.模块是Erlang项目中代码组织的基本方式. 2 -compile([export_all]). 指明编译选项,export_all 用来导出所有本模块中的函数,exported function是模块的接口,其他模块只能调用exported function 4 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 2,实现sleep: receive after Time -> ok end 4,检测是否存在消息: receive SomeMsg -> exist after 0 -> no_exist end 1, 阻塞等待任意消息: receive Msg -> ok end 3,等待消息,超时为5 sec: receive Msg -> ok after 5000 -> timeout end
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 节点A2连接节点B2步骤 • 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'T Behaviours 通过定义一些简单的callback模块实现特定功能. • application - 定义application,实现某种功能,由其他behaviours组成 • supervisor - 定义一个supervisor tree,实现各种策略的任务重启机制 • gen_server - 定义一个通用的server模型,一个process loop,提供同步异步接口 • gen_fsm - 实现一个状态机 • gen_event - 实现一个event manager及event handler模型
与其它语言的交互 • External App • 外部应用崩溃不会影响Erlang虚拟机 • Ports - 通过port与外部应用交互(stdin/stdout) • 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't get_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
产品开发流程 • 架构设计 • 单台 or 分布式? Master-Slave or Grid? Monitor, Failover, Net Comunication, Database, Replica ... • OTP Behaviourhow many application?use supervisor, gen_server, gen_fsm, gen_event • Coding, 及单元测试用例. 每个module都经过测试(eunit) • 编写系统测试框架,覆盖测试,确保系统正确(common test) • 压力测试,分析性能瓶颈,进行优化(fprof) • 系统上线,监控功能(ganglia, nagios, monit) • 新的功能或需求,重复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 Mailist http://www.nabble.com/Erlang-Questions-f14096.html Erlang Planet http://www.planeterlang.org/ Erlang非业余研究 http://mryufeng.javaeye.com/ Erlang Display http://erlangdisplay.javaeye.com/
开源项目 ejabberd - the Erlang Jabber/XMPP daemon RabbitMQ - AMQP server CouchDB - schema-free document database Tsung - multi-protocol distributed load testing Scalaris - distributed key-value store Disco - MapReduce Framework Mochiweb - Powerful Http Server Tookit 自己动手发起erlang开源项目!
案例 • AXD301 • 高并发的电信交换机 • 99.9999999% 可靠性(~3ms 故障/年) • 超过100万行Erlang代码 • 软实时系统 • 高容错
案例 • WEB IM后台(mochiweb) • 7+ 百万活跃用户 • ~100 server • ajax + comet(long-polling)