Erlang学习笔记
转:http://cryolite.iteye.com/blog/419235
1.receive子句的消息匹配模式:如果消息很简单,使用atom就可以描述的话,没必要使用tuple,tuple会消耗更多的内存,而且减慢了进程的处理速度。
2.i()列出当前进程的详细信息,regs()列出所有注册进程和端口的详细信息。ets:i()列出所有ets表的详细信息。
3.atom不会被垃圾回收,因此为所有进程都进行注册有可能会耗尽内存。建议只对长生命周期的进程进行注册。
4. 给pid发送消息不会有任何错误,哪怕该进程不存在;但是,当注册的进程不存在时,通过注册名给它发消息会出错。
5. 进程对接收到的消息不进行处理(匹配)应视为一种bug,因为这些未被处理的消息会不断的在进程的mailbox里堆积,最后引起内存泄露,然后,系统崩溃。mailbox过大的另一个问题是,每次收到新消息,所有的旧消息都要过一遍,从而减缓了进程的处理速度,表现为CPU占用率很高。
此外,大容量mailbox的进程还会迟滞那些发消息给它的进程(因为Erlang runtime为了使大容量mailbox的进程能及时进行处理,它会主动放慢那些给这个进程发送消息的进程) 但是,丢弃这些消息又会使调试困难,因此,最好确保在日志中记录下这些不能处理的消息。
通过优化代码、对OS和Erlang VM精心调试可以纠正此类问题。 另外同步化发送消息的进程是一种办法,发送进程发送一个消息然后等待一个确认会减慢maibox的消息增长速度。
同步化的副作用是减少了接收处理进程的最大吞吐量,但至少不会使系统当掉。
6. Erlang无法完全避免死锁,例如进程A同步调用进程B(A给B发送消息,然后等待B返回响应消息),后者(B)又同步调用进程A。
7. 进程的优先级可以通过调用process_flag(priority, Priority)进行调整,但是不鼓励调整进程的优先级,甚至应该完全禁止使用。
8. 进程间的联系有两种,一种是双向的,即link;一种是单向的,即monitor。
前者建立的联系是永久的,后者是临时的。前者只能给存在的进程建立联系,后者可以监视不存在的进程(当然会立即收到一个{'DOWN'...,noproc}消息)。
显然后者适合用在不对称关系的进程中,例如client-server
9. 解除对进程的监视最好调用erlang:demonitor(Reference, [flush]),因为demonitor调用之前监视的进程可能就DOWN掉了。
10. 任一进程非正常退出,会给其link的进程集发出exit信号,exit信号将像多米诺骨牌一样传递出去(每张牌就是一个进程,倒掉代表进程被结束)。
可以理解成:调用process_flag(trap_exit,true)后,进程将收到的其它进程exit信息转换成{'EXIT', Pid, Reason}消息,从而制止了多米诺骨牌的继续倒掉;
11. 进程的正常结束不会引发关注:进程正常退出时,也会给它的直接link set发送exit信号,但这个正常退出信号不会进一步的传播下去,也就是说正常退出信号不会引发多米诺骨牌的倒塌。
(设置了trap_exit标志的进程会将这个exit信号转换成消息{'EXIT',Pid,normal})。
12. 两个在不同计算机上的link进程,如果它们之间的网络连接断掉了,会收到{'EXIT', Pid, noconnection}的消息
13. 大型的Erlang系统中不应该有孤儿进程,也就是说所有的进程都要连接在一棵巨大的supervision树上。
孤儿进程有两个问题,首先不容易发现bug:当这些进程因bug当掉时,根本无从知晓。另一个问题是“进程泄漏”,孤儿进程因为某种原因挂了起来,它会一直挂着,随着越来越多的进程挂起,这些不断积累的孤儿进程终将耗尽内存。
14. supervisor进程的唯一任务就是启动子进程并监视之。supervisor设置成trap_exit,并link到所有子进程。
15. Erlang的在线升级,分两种情况。
第一种情况是模块A在进程中运行,A会调用模块B的函数,运行A模块的进程会维持一个到模块B的最新版本的链接(link),在模块B重载后,进程中模块B的链接(link)会切换到最新版本,这样,模块A以后会自动调用更新后的函数。
第二种情况是模块A本身的升级,这种正在进程中运行的模块的升级要复杂一些,取决于模块中函数的调用方式:是直接调用,还是fully qualified function call调用(指B:fun1()这样的调用),如果是直接调用,在模块重载后,还是维持旧版本的模块,而 fully qualified函数调用将会立即切换到最新版本的函数。 16. Erlang运行时只维持两个版本的模块代码,因此在第2次模块升级后,最老版本的模块会被移除,如果有进程仍然运行最老版本的模块,这些进程将随着最老版本模块的移除而被终止(killed)。
17. 有三种装载模块的方法。
第一种是直接调用模块中的函数,此时一个叫code server的进程(它是Erlang kernel的一部分)将会搜索相应的beam文件,然后装载之;
第二种是编译该模块(相应的函数是compile:file(Module))后会自动装载编译好的模块,在shell中是通过命令c(Module);
第三种方式是显式的装载模块,通过调用code:load_file(Module)转载指定的模块,在本机上可以通过命令l(Module)装载模块,在网络中可以通过命令nl(Module)将模块装载到各个节点上。
18. Erlang运行时,同一个模块至多维持有两个:旧的和当前的。当又有新的版本装载进来,旧的那个会被清除,当前的变成旧的,刚装载上的成为当前的。
其中旧的、当前的模块与该模块实际版本无关,而与装载的时间顺序有关(可能先装载一个最新版本的模块,之后又装载老版本的模块,这种情况下先装载的模块是旧的,后装载的模块是当前的)。
19. shell中按tab键会显示所有已装载的模块。
20. 调用code:is_loaded(Module)判断模块是否已装载。shell中通过tab键补全特性判断模块是否已装载。
21. code server的主要任务是动态的装载和清除模块。通过code模块中的函数可以操作code server,进而对系统代码库进行管理和配置。
22. code search path是code server装载模块是的搜索路径,缺省包括当前路径,以及所有库应用的路径。通过code:get_path()查看。
23. Erlang根目录:即Erlang安装目录,所有库应用都在根目录的lib目录下,通过code:root_dir()可以查看根目录位置。
24. 热升级面临的几个问题:升级步骤原子化;向后兼容以备升级失败;分布式环境中升级的同步化。SASL应用库提供了一些热升级的工具。
25. 最简单的热升级是重载模块即可。前提条件是升级不会修改已存在的loop data。
26. 既然函数也是一种数据,那么显然,它也可以是tuple的一部分、record的一部分,也可以作为消息进行发送。当然,作为其它函数的返回值也就没什么奇怪的,这样就可以在运行时动态的创建函数了。
27. reference数据:在分布式环境中reference数据提供了一个跨进程的唯一标识(tag),它主要用于进行比较,多于消息传递有关。
28. 用变量绑定已定义在模块中的函数:Fun1 = fun Module:Function/Arity
29. 将函数传递给远程节点时,显然,函数中调用的那些模块必须也在远程节点的code search path中。
30. 一个binary是一块无类型的内存的引用。最初被Erlang用来通过网络加载代码,随后就应用在基于socket的通讯中。
31. ets的match操作有可能会损害Erlang系统的实时性:这是因为所有的match操作都是作为BIF实现的,而BIF的执行具有原子性,当对较大的ets表进行match操作时要遍历完整个ets表,这就会堵塞其它进程。要避免此类问题最好用first、next遍历ets表,这样做也许会更慢,但至少不会损害系统的实时性。
32. 用ets存储record数据要指定key位置,因为作为record的tuple第一个元素总是相同的。
33. 一个Erlang节点是指运行中的Erlang运行时系统。一个alive的Erlang节点是指“能够”与其它节点通讯的节点,alive节点必须有名字。
调用erlang:is_alive()判断当前节点是否alive。net_kernel模块提供了控制Erlang节点alive的函数。
34. alive的Erlang节点名字有两种:短名(short name)和长名(long name),短名节点只能与短名节点通讯,长名节点只能与长名节点通讯。
35. Erlang系统的安全理念是:要么完全信任你,要么完全不信任你。因此互联的远程Erlang节点拥有本地节点的所有访问权限。对于互联网上分布的Erlang节点,可以通过SSL加强安全性。
36. 节点间的安全是通过cookie控制的,每个节点持有一个被称为secret cookie的atom,持相同cookie的节点间才能进行通信。
(home目录下有个“.erlang.cookie”文件,其内容为缺省cookie值。)
另外,通过临时设置cookie可以让拥有不同cookie的节点间互通。
37. 分布式节点间的互联是通过每个节点上的net_kernel进程实现的,进程的远程启动也是net_kernel负责。
net_kernel进程通过cookie进行安全授权。因此用户可以通过修改net_kernel定制自己的安全授权机制。 38. 对于有太多Erlang节点的分布式系统,一个问题是会有太多的TCP/IP连接,如果有N个节点,这些阶段彼此联通,会有N * (N - 1) / 2个TCP/IP连接。解决办法是hidden node。
39. 通过node(hidden)或node(connected)查看hidden nodes
40. hidden node的一个用法是作为“网关”将许多小的分布式机群连接起来。另一个用法是用来做维护,它不会影响流量。
41. 通用的消息发送格式是Pid ! Message,给远程注册进程发消息是:{register_name, node} ! Message
42. receive的timeout一个麻烦的地方是可能超时后消息又到了,这时消息就会堆积在接收进程的mailbox中,在该进程又一次进入消息接收时,可能处理的是这些超时后堆积的消息,因此进行消息接收之前最好flush一下进程的mailbox。
43. monitor_node(RemoteNode, true)用于监控远程节点,当被监控的远程节点当掉时监控进程会收到{nodedown, RemoteNode}消息。
44. 查询某个进程(Pid)、某个引用(Reference)或者某个端口(Port)在哪个节点上,可以使用BIF函数node(Arg)。
45. epmd是Erlang port mapper deamon的缩写,这是一个操作系统级的守护进程,是Erlang运行时系统的一部分。
不管运行了多少Erlang节点,每个计算机只运行一个epmd,它随Erlang节点的第一次启动而启动,不随Erlang节点的结束而结束。
epmd负责监听4369端口上的连接请求,然后将其映射给相应节点的监听端口。
46. epmd可以作为shell命令单独启动,该命令还带若干参数,可以通过 epmd -help查看。例如可以查看所有erlang节点的占用端口。
47. epmd的一个重要用处是通过参数delay_accept和delay_write可以模拟网络繁忙的情况,便于测试。
48. 程序员编写的OTP应用(OTP application)是构成Erlang系统的砖石,它在运行时的进程组成一个supervision树。OTP应用本身是一种OTP behavior。