动态代码载入

每当调用 someModule:someFunction(…) 时,调用的总是最新模块里的最新版函数,哪怕当代码在模块里运行时重新编译了该模块也是如此。

在任一时刻,Erlang允许一个模块的 两个版本 同时运行。

保存预处理的输出

erlc -P some_module.erl

它会生成 some_module.P 文件

表达式和表达式序列

任何可以执行并生成一个值的事物,都被称为表达式。

表达式序列,是由一系列逗号分隔的表达式。它们在 -> 箭头之后随处可见。表达式序列 E1, E2, E3,…,En 的值,被定义为序列最后那个表达式的值,而该表达式在计算时可以使用 E1, E2 等表达式所创建的绑定。定价于 LISP 里的 progn

函数引用

  • fun LocalFunc/Arity
  • fun Mod:RemoteFunc/Arity

包含文件

-include(FileName).

包含库的头文件:

-include_lib(Name).

列表操作

++

A ++ B

即 A 列表和 B 列表相加

1> [1,2,3] ++ [4,5,6].
[1,2,3,4,5,6]
2>

A -- B

从列表 A 中移除列表 B 。移除的意思是 B 中所有元素都会从 A 里去除。请注意,如果符号 X 在 B里出现了 K 次,那么A只会移除前K个X。

2> [1,2,3,4,5,6,1,2] -- [1].
[2,3,4,5,6,1,2]
3> [1,2,3,4,5,6,1,2] -- [1,1].
[2,3,4,5,6,2]
4>

短路布尔表达式

  • Expr1 orelse Expr2
  • Expr1 andalso Expr2

下面的并不是短路表达式:

A or B
A and B

这种两边的参数总会被执行,即使表达式的真值只需要第一个表达式的值就能确定也是如此。

设置代码搜索路径

查看

code:get_path().

设置

erl -pa Dir1 -pa Dir2 .... -pz DirK1 -pz DirK2

pa 表示添加到搜索的开头 pz 表示添加到搜索的结尾

导出所有函数

-compile(export_all).

系统启动时执行一组命令

在用户主目录的 .erlang 文件里的所有命令,都会有 erlang 启动时执行:

[12:41:01] emacsist:~ $ cat ~/.erlang
io:format("Hi, I'm in your .erlang file ~n").
[12:41:05] emacsist:~ $ erl
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Hi, I'm in your .erlang file
Eshell V8.3  (abort with ^G)
1>

查看用户主目录位置:

init:get_argument(home).

在命令提示符里编译和运行

erlc hello.erl
erl -noshell -s hello start -s init stop

使用 Escript 运行

文件 hello 的内容:

#!/usr/bin/env escript
main(Args) ->
    io:format("hello world~n").

Args 是一个原子列表

执行:

chmod u+x hello
./hello

停止Erlang

有时Erlang停不了下来,以下是可能的原因

  • shell 没反应
  • Ctrl-C 被禁用
  • erlang 启动时设置了 -detached 标识
  • erlang 启动时设置了 -heart Cmd 选项。它会建立一个操作系统进程来监控Eralng的操作系统进程。如果Eralng的操作系统进程崩溃了,Cmd就会被执行。通常Cmd只是简单地重启Erlang系统。
  • 某处出现了严重的问题,导致Erlang进程为僵尸进程

分析转储文件 erl_crash.dump

crashdump_viewer.start().

获取帮助

erl -man erl
erl -man lists

基本并发函数

Pid = spawn(Mod, Func, Args)
Pid = spawn(Fun)


Pid ! Message

receive ... end

带超时的接收

receive 
    ...
after Time ->
     Expressions
end

Time 为毫秒数

注意, receive 里面的表达式可以忽略。而只保留超时

receive
after T ->
    Exp
end. 

如果 Time 为0,则会先尝试匹配 receive 里的模式,如果没有匹配,则直接执行 after 的表达式,而不会挂起。

获取最大进程限制数

erlang:system_info(process_limit)

这个数可以通过虚拟机参数 +P 100000 来设置

注册进程

  • register(AnAtom, Pid)
  • unregister(AnAtom) 移除 AnAtom 的注册信息。某个注册进程崩溃了,就会自动取消注册
  • whereis(AnAtom) -> Pid | undefined 查看AnAtom是否已经被注册。
  • registered() -> [AnAtom::atom()] 返回所有注册进程列表

并发Erlang的错误处理

它是建立在 远程检测处理错误 的概念之上。即选择让进程崩溃,然后在其他进程里纠正错误。

监控进程可以 实现跨机器 的透明动作,这是编写容错式系统的基础。不能在同一台机器上构建容错式系统。

即如果某个 Erlang 进程出了点小问题,可以尝试用 catch 或 try 语句来修复它。但如果修复失败,就应该直接崩溃,让其他进程来修复这个错误。

进程链接

调用函数 link(Pid) 就会在调用进程和Pid之间创建一个链接。

注意,链接是 双向 的。如果 P1 调用了 link(P3) ,则 P1 和 P3 就是相互链接了。

设立防火墙

即不想系统里的错误继续扩散

在某个进程上,执行 process_flag(trap_exit, true) 这样子,错误扩散到该进程,就不会继续扩散了。

监视

监视是 单向 的。如果A监视B,而B挂了,就会向A发送一个退出消息,反过来而不会如此。

RPC调用

rpc:call(Node, Mod, Func, [Arg1, Arg2, Arg3,...,ArgN]).

设置:

erl -setcookie ABSFSDFSADFSDFSDFSDFSF 

这一般只是在测试时使用,因为使用 ps aux 可以查看它的 cookie 。

或保存在 ~/.erlang.cookie

或使用函数来设置: erlang:set_cookie(node(), C) 即将cookie设置为C

使用套接字的分布式模型

lib_chan 模块

Erlang中的端口

通过它来控制外部进程以及通信。Erlang负责启动和停止外部程序,还可以监视它,在它崩溃后重启。外部进程,称为 端口进程 ,因为它是通过一个 Erlang 端口控制的。

使用端口与外部进程,和使用套接字是不同的。如果使用端口,它会表现得像一个 Erlang 进程,这样就可以链接它,从某个远程分布式 Erlang 节点向它发送消息等等。如果使用套接字,就不会表现出类似进程的行为。

相连进程

创建端口的进程被称为该端口的相连进程。所有发往端口的消息,都必须标明相连进程的PID,所有来自外部程序的消息都会发往相连进程。

创建端口

open_port(PortName, [Opt]) -> Port

向端口发送消息

Port ! {PidC, {...}}

Erlang中调用外部语言代码

这是一种不安全的做法。

ETS与DETS

它们可以在进程间共享!(但它不会被GC)

它们保存的,都是元组。默认情况下,第一个是该表的 键。

CRUD: - ets:new或dets:open_file - insert(TableId, X) X为元组,或元组列表 - lookup(TableId, Key) - dets:close(TableId) 或 ets:delete(TableId)

表的四种类型:

  • set
  • ordered_set
  • bag
  • duplicate_bag

DETS的最大文件大小是 2GB

Mnesia数据库

它是用Erlang编写的数据库。 它支持事务,并有自己的查询语言。支持保存在内存,也可以保存在磁盘。在集群里,不同的节点可以是不同的存储类型。

它保存的是 Erlang 中的 记录 (record)

初始化

mnesia:create_schema([node()]).

在非集群里,它会自动创建一个 Mnesia.nodename@hostname 的目录来保存数据库。也可以在启动时,指定保存的目录,而不是默认的 Mnesia.nodename@hostname ,像这样子:

erl -mnesia dir '"/home/emacsist/Mnesia"'

注意,单引号里面的双引号。

启动

mnesia:start()

其他的就不继续写的,详细直接看文档。

代码性能分析工具

  • cprof :统计各个函数被调用的次数。在 online 系统上运行,会增加 5~10%的负载
  • fprof :显示调用和被调用函数的时间,结果会输出到一个文件。重量分析,会显著增加系统负载
  • eprof :测量erlang程序是如何使用时间的。它是 fprof的前身,适用于小规模的性能分析。

    cprof:start().
    
    执行你的代码 Mod:xx
    
    cprof:pause().
    
    cprof:analyse(Mod)
    
    cprof:stop().
    

代码覆盖测试

cover:start().

cover:compile(你的模块).

你的模块:函数().

执行一段时间后。。。。。

cover:analyse_to_file(你的模块).

生成交叉引用

注意,只有在代码编译时设置了 debug_info 才可以使用 xref 模块

erlc +debug_info *.erl
erl
1>xref:d('.').

跟踪消息与进程执行

参考手册下面的这个文档

erlang:trace/3

配置错误记录器

标准错误记录器

erl -boot start_clean

它创建一个适合进行开发的环境,只提供一种简单的错误记录形式。(不带参数的 erl 命令,就等同 erl -boot start_clean)

erl -boot start_sasl

它会创建一个适合运行生产系统的环境。