世界是并行的,Erlang程序反应了一种思考和交流的方式,个体通过发送消息进行交流,如果有个体死亡,其他个体会注意到。 Erlang的模块类相当于OOPL中的类,进程类似于OOPL里的对象或类实例。并发编程可以用来提升性能,创建可扩展和容错的系统,以及编写清晰和可理解的程序来控制现实世界里的应用。 并发程序是以一种并发编程语言编写的程序,并发编程语言拥有用于编写并发程序的语言结构。Erlang的并发程序是由互相通信的多组顺序进程组成,一个进程就是一个轻量级的虚拟机,可以执行单个的Erlang函数,只能通过发送和接收消息来与其他进程通信。也就是说,并发性是由Erlang虚拟机提供的,比操作系统的并发控制粒度要小很多。 在Erlang中:
动态代码载入是Erlang特性之一,总是调用最新模块中的最新函数,哪怕当代码在模块里运行时重新编译了该模块也是如此。 基本元素操作Erlang shell中,用句号加空格、tab或回车来结束表达式,%表示注释的起点,;隔离子句。模块是.erl 文件,库的头文件是.hrl, shell中的编译是c(),外编译命令是erlc, 退出shell用q(),或erlang:halt(). 变量以大写字母开头,且不能重新绑定变量,只能一次性赋值,具有不可变状态。原子元素是全局的,不需要宏定义或包含文件,以小写字母开头,还可放在单引号内,是极简表达式。 元组(tuple)是一些数量固定的项目归组成单一实体{,}, 由于是匿名的,通常在第一个元素上贴标签,来增加可读性。提取元组中的值使用模式匹配操作符=,为匿名变量,多个不必绑定相同的值。但是,_Mode则是常规变量。例如: 2> Family={family,father,mother,son}.
{family,father,mother,son}
3> {_,X,Y,Z}=Family.
{family,father,mother,son}
4> X.
father
5> Y.
mother
6> Z.
Son 记录(record)是元组的另一种形式,可以给元组的各个元素关联一个名称。使用记录的情形: 列表(list)形如[,,]可以存放任意数量的事物。Head可以是任何事物,Tail通常仍然是个列表。只要用[…|T]构建一个列表,就应确保T是一个列表。同样使用模式匹配来提取列表中的元素。列表推导的常规形式: [X||Qualifier1,Qualifier2,…] X是任意一表达式,限定符qualifier可以生成器,位串生成器或过滤器。生成器的写法 Pattern<- ListExpr 反转一个列表时,要调用lists:reverse. Erlang中没有字符串,字符串是个整数列表,”HelloCloud”是一个列表的简写,io:format来指定打印输出。 映射组(map)是键值对的关联集合,内部为有序存储,适用情形: 映射组的语法: #{key1 op val1,key2 op val2,…,KeyN op valN} '#后没有名称,op是=>或:=之一。 => 将现有键值更新为新值或给映射组增加一个新键值对。 :=用于更新。 键不能包含任何变量,值可以包含未绑定变量,在模式匹配成功后绑定。 映射组可以通过io:format 里的~p选项输出,并用io:read 或file:consult读取。 Json与映射组的对应关系: 模块与模式匹配模块是Erlang的基本代码单元,erl文件编译后以.beam作为扩展名,采用UTF8字符集,.erl文件示意如下: -module(模块名,与存放模块的文件名相同)
-export([方法名/输入参数的个数])
Method1( {a,b,c})->a*b*c;
Mehtod2({d,e})->d-e. 模块属性有两种类型:预定义型和用户定义型。 Erlang中用于代表函数的数据类型被称为fun,相当于Python中的lambda,一般用于
模式匹配是Erlang的根基,case和if表达式使Erlang代码小而一致。 case of
Pattern1[ when Guard1] -> Expr-seq1;
Pattern2[when Guard2]-> Expr-seq2;
…endif
Guard1-> Expr_seq1;
Guard2-> Expr_seq2;
…end Erlang有两种方法来捕捉异常错误,一种是把抛出异常的调用函数封装在一个try_catch 表达式里,提供了概括信息,另一种是把调用封装在一个catch表达式里,提供了详细的栈跟踪信息。在捕捉到一个异常后,可以调erlang:get_stacktrace()来找到最近的栈信息。 把二进制型,位串,和位级模式匹配引入Erlang是为了简化网络编程。二进制型是置于双小于号和双大于号之间的一列整数或字符串。 Term_to_bingary(Term) ->Bin 转换为二进制型 运行运行Erlang程序的方式:
#!/bin/shErl –noshell –pa /home/abel/practice/erlang/code –s hllstart –s init stop
#!/usr/bin/env escript 内置函数apply能调用某个模块的某个函数并传参。每个erlang进程都有一个被称为进程字典的私有数据存储区。为了增强类型的表达能力,可以用描述性变量给它们加上注解,类型规范为spec,类型说明type。通过dialyzer可以检查程序中的类型错误,最好写模块时先考虑类型并声明它们,然后编写代码。两个载入路径的函数: -spec code:add_patha(Dir)=>true|{error:bad_directory} 载入路径头加入 Make 是erlang的任务自动化工具,可以通过它来运行程序。下面是一个简单的makefile: .SUFFIXES: .erl .beam
.erl .beam:
erlc -W $<
ERL = erl –boot start_clean
MODS = module1 module2 module3all: compile
$(ERL) –pa 'home/abel/…/dir’–s module1 start
compile: ${MODS:%=%.beam}
clean:
rm -rf *.beam erl_crash.dump
如果Erlang程序崩溃了,会留下一个erl_crash.dump文件,可以通过web故障分析器来分析,命令如下: 1> crashdump_viewer:start(). 并发Erlang中基本的并发函数: receive
Pattern1[when Guard1]-> 1;
Pattern2[whenGuard2]->2;
…
aftertime->
s end. 内置函数erlang:system_info(process_limit)可找出所允许的最大进程数,默认为262144. 并发程序模板: -module(ctemplate).-compile(export_all).
start() ->
Spwan(?MODULE,loop,[]).
rpc(Pid,Request) ->
Pid! {self(),Request},
receive
{Pid,Respone}->
Response end.
loop(X) ->
receive
Any-> Io:format(“Received:~p ~n”, [Any]),
loop(X) end. 每当收到消息时会处理它并再次调用loop(),这一过程称为尾递归,无需消耗堆栈空间可以一直循环下去。 Erlang并发程序的错误处理建立在远程监测和处理错误的基础上,重点在补救而不是预防,几乎没有防御性代码,只有在错误后清理系统的代码,即让其他进程修复错误和任其崩溃。 程序在出错时立即崩溃的优点: -spec spwan_link(Fun) ->Pid
-spec spwan_monitor(Fun)-> {Pid,Ref}
-spec process_flag(trap_exit,true)
-spec link(Pid) ->true
-spec unlink(Pid) -> true-spec erlang:monitor(process,Item) ->Ref
-spec exit(Why) -> none() 分布式模型:分布式erlang 和基于socket的分布式模型。分布式erlang运行在可信网络,通常在同一局域网的集群上,并受防火墙保护。基于socket的分布式模型基于TCP/IP的不可信网络.
为了在互联网上执行并发程序: $erl -name …-setcookie … -kernelinet_dist_listen_min Min RPC提供了许多远程调用服务,global里的函数可以用来在分布式系统里注册名称以及维护一个全连接的网络。 $erl -setcookieABCDEFG2048 3) 内置函数erlang:set_cookie(node(),C)在程序中指定 Erlang通过名为端口的对象与外部程序通信,如果想端口发送一个消息,这一消息就会被发往与端口相连的外部程序,来自外部程序的消息会变成来自端口的Erlang消息。创建端口的进程成为端口的相连进程,所有发往端口的消息都必须标明相连进程的PID,所有来自外部程序的消息都会发往相连进程。 socket 编程简例Erlang 中gen_tcp 用于编写TCP程序,gen_udp用于编写UDP程序。一个简单的TCP服务器echo示例: Start_echo_server()->
{ok,Listen}= gen_tcp:listen(1234,[binary,{packet,4},{reuseaddr,true},{active,true}]),
{ok,socket}=get_tcp:accept(Listen), gen_tcp:close(Listen),
loop(Socket).
loop(Socket) ->
receive
{tcp,Socket,Bin} -> io:format(“serverreceived binary = ~p~n”,[Bin])
Str= binary_to_term(Bin), io:format(“server (unpacked) ~p~n”,[Str]),
Reply= lib_misc:string2value(Str), io:format(“serverreplying = ~p~n”,[Reply]), gen_tcp:send(Socket,term_to_binary(Reply)),
loop(Socket);
{tcp_closed,Socket} -> Io:format(“ServerSocket closed ~n”) end. TCP的echo客户端示例: echo_client_eval(Str) ->
{Ok,Socket} = gen_tcp:connect(“localhost”,2345,[binary,{packet,4}]),
ok= gen_tcp:send(Socket, term_to_binary(Str)),
receive
{tcp,Socket,Bin}-> Io:format(“Clientreceived binary = ~p~n”,[Bin]),
Val=binary_to_term(Bin), io:format(“Clientresult = ~p~n”,[Val]), gen_tcp:close(Socket) end. UDP server示例 udp_demo_server(Port) ->
{ok,Socket}= gen_udp:open(Open,[Binary]),
loop(Socket).
Loop(Socket)->
receive
{udp,Socket,Host,Port,Bin}->
BinReply= …,
gen_udp:send(Socket,Host,Port,BinReply),
loop(Socket)
End. UDP client 示例: udp_demo_client(Request) ->
{ok,Socket}= gen_udp:open(0,[Binary]),
ok= gen_udp:send(Socket,”localhost”,1234,Request),
Value= receive
{udp,Socket,_,_,Bin}-> {ok,Bin}
after2000 -> error end, gen_udp:close(Socket),
Value 注意,因为UDP是不可靠的,一定要设一个超时时间,而且Reqeust最好小于500字节。 OTPOTP包含了一组库和实现方式,可以构建大规模、容错和分布式的应用程序,包含了许多强大的工具,能够实现H248,SNMP等多种协议,核心概念是OTP行为,可以看作一个用回调函数作为参数的应用程序框架,类似一个J2EE容器。行为负责解决问题的非函数部分,回调函数负责解决函数部分。 通过gen_server模块可以实现事物语义和热代码交换,
当服务器崩溃时,需要一种机制来检测并重启它,要用到监测树,即创建一个监控器来管理服务器。监测树有两种:一对一和一对多。 使用gen_server,gen_supervisor,application等行为,可以构建可靠性为99.9999999的系统。
Erlang程序在多核CPU上运行 -specmapreduce(F1,F2,Acc0,L) ->Acc
F1 = fun(Pid,X) ->void
F2 = fun(Key,[Value],Acc0) ->Acc
L = [X]
Acc = X =term()
Mapreduce 是在并行高阶函数(phofs)模块中定义的。 朝花夕拾,前辈神人保佑我们产品的进程! |
|