搬运下存到云笔记的东西
第一部分 为何使用Erlang
-module(atom) 意思是此文件包含用于atom模块的代码。它应该与文件名一
致(除了.erl这个文件扩展名)
-export([func/1]) 导出声明指明了模块里哪些函数可以从模块外部进行调用
函数名(func)/传参个数(1)
Pid ! Msg语法的意思是发送消息Msg到进程Pid。
接收消息
receive
{From, Meg} ->
:ok
after Time ->
ok
end
receive 只会遍历邮箱一次;下一次遍历,是在受到新消息的时候
遍历邮箱的时候,匹配到一个,立刻结束匹配的过程,不回继续进行
不加after语句的话,receive 遍历邮箱完毕,如果没有匹配到,就会阻塞在receive这里;如果匹配到了,就会执行receive end后面的代码块
第二部分 顺序编程
第三章
启动erl shell $ erl
退出erl shell $ q().
c(mod). 编译mod.erl文件
查看帮助 shell 里输入 help()
q()是init:stop()命令在shell里的别名。要立即停止系统,应执行表达式erlang:halt()
shell 详细命令说明: https://blog.csdn.net/mycwq/article/details/16858805
Erlang可以用任意长度的整数执行整数运算。在Erlang里,整数运算是精确的,因此无需担
心运算溢出或无法用特定字长(word size)来表示某个整数。最大数和内存挂钩。
大写表示变量,小写是atom, {}是tuple, [] 是list, “” 是string
=不是一个赋值操作符,而是一个模式匹配操作符
在Erlang里,变量只不过是对某个值的引用: Erlang的实现方式用指针代表绑定变量,指向
一个包含值的存储区。这个值不能被修改
第四章 模块与函数
多条子句用;分隔
add([A, B]) -> A + B;
add([A, B, C]) -> A + B + C.
逗号(,)分隔函数调用、数据构造和模式中的参数。
分号(;)分隔子句。我们能在很多地方看到子句,例如函数定义,以及case、 if、try..catch和receive表达式。
句号(.)(后接空白)分隔函数整体,以及shell里的表达式
shell 中写函数
13> Add = fun(A, B) -> A + B end.
#Fun<erl_eval.12.99386804>
14> Add(1, 2).
3
列表推导
17> L = [1, 2, 3, 4].
[1,2,3,4]
18> [2 * X || X <- L].
[2,4,6,8]
19>
第五章 记录与映射组
元组用于保存固定数量的元素,而列表用于保存可变数量的元素
记录(record)其实就是元组(tuple)的另一种形式,因此它们的存储与性能特性和元组一样。
映射组(map)比元组占用更多的存储空间,查找起来也更慢。而另一方面,映射组比元组要灵活得多
文件包含是唯一能确保多个Erlang模块共享相同记录定义的方式。它类似于C语言
用.h文件保存公共定义,然后包含在源代码文件里
要在shell里这么做,必须先把记录的定义读入shell,然后才能创建记录。我们将用shell函数
rr(read records的缩写,即读取记录)来实现
λ vi records.hrl
-record(todo, {status=reminder, who=joe, text}).
1> rr("records.hrl").
[todo]
2> #todo{}.
#todo{status = reminder,who = joe,text = undefined}
3> X1 = #todo{status=urgent, text="11111"}.
#todo{status = urgent,who = joe,text = "11111"}
4> X2 = X1#todo{status=done}.
#todo{status = done,who = joe,text = "11111"}
5> #todo{who=W, text=Txt} = X2.
#todo{status = done,who = joe,text = "11111"}
6> W.
joe
7> Txt.
"11111"
8> X2#todo.text.
"11111"
9> rf(todo).
ok
10> X2.
{todo,done,joe,"11111"}
12> F1 = #{a => 1, b => 2}.
#{a => 1,b => 2}
要基于现有的映射组更新一个映射组,我们会使用如下语法,其中的Op(更新操作符)是=>或:=:
表达式K := V的作用是将现有键K的值更新为新值V。 如果被更新的映射组不包含键K,这个操作就会失败
14> F3 = F1#{c => xx}.
#{a => 1,b => 2,c => xx}
15> F4 = F1#{c => xx}.
#{a => 1,b => 2,c => xx}
16> F5 = F1#{c := xx}.
** exception error: {badkey,c}
in function maps:update/3
called as maps:update(c,xx,#{a => 1,b => 2})
in call from erl_eval:'-expr/5-fun-0-'/2 (erl_eval.erl, line 255)
in call from lists:foldl/3 (lists.erl, line 1263)
第六章 顺序程序的错误处理
异常错误发生于系统遇到内部错误时,或者通过在代码里显式调用throw(Exception)、
exit(Exception)或error(Exception)触发
用 try...catch 捕捉异常错误
捕捉到一个异常错误后,可以调用erlang:get_stacktrace()来找到最近的栈跟踪信息
第七章 二进制型与位语法
在大多数情况下,二进制型里的位数都会是8的整数倍,因此对应一个字节串。如果位数不
是8的整数倍,就称这段数据为位串(bitstring)。所以当我们说位串时,是在强调数据里的位数
不是8的整数倍。
二进制型的编写和打印形式是双小于号与双大于号之间的一列整数或字符串
list_to_binary与binary_to_list 不可逆,list_to_binary会简化列表
用term_to_binary和binary_to_term
假设要把三个变量(X、 Y和Z)打包进一个16位的内存区域。 X应当在结果里占据3位, Y应
当占据7位,而Z应当占据6位。在大多数语言里这意味着要进行一些麻烦的底层操作,包括位移
位(bit shifting)和位掩码(bit masking)。而在Erlang里,只需要这么写:
M = <<X:3, Y:7, Z:6>>
这段代码会创建一个二进制型并把它保存在变量M里。请注意: M的类型是binary,因为数
据的总长度是16位,可以被8整除。如果换一种写法,把X的大小改成2位:
M = <<X:2, Y:7, Z:6>>
那么M的总位数是15,因此最终数据结构的类型是bitstring
第八章 顺序编程补遗
apply
它通过函数名和参数计算该函数的值,其中的函数名和模块名是动态计算得出的。
算术表达式
这里定义了所有合法的算术表达式。
元数
一个函数的元数是这个函数所接受的参数数量。
属性
这一节涉及Erlang模块属性的语法和解释。
块表达式
它们是使用begin和end的表达式。
布尔值
它们是用原子true或false表示的事物。
布尔表达式
这一节介绍了所有的布尔表达式。
字符集
这里介绍了Erlang使用的字符集。
注释
这一节介绍了注释的语法。
动态代码载入
这一节介绍了动态代码载入的工作原理。
Erlang的预处理器
这一节介绍了Erlang在编译前发生了什么。
转义序列
这一节介绍了转义序列的语法,它被用于字符串和原子。
表达式和表达式序列
这一节介绍了表达式和表达式序列是如何定义的。
函数引用
这一节介绍了如何引用函数。
包含文件
这一节介绍了如何在编译时包含文件。
列表添加和移除操作符
它们是++和--。
宏
这一节介绍了Erlang的宏处理器。
模式的匹配操作符
这一节介绍了如何将匹配操作符=用在模式中。
数字
这一节介绍了数字的语法。
操作符优先级
这一节介绍了所有Erlang操作符的优先级和结合性。
进程字典
每个Erlang进程都有一个本地的破坏性存储区域,有时候会很有用。
引用
引用是独一无二的符号。
短路布尔表达式
这些是不完全求值的布尔表达式。
比较数据类型
这一节介绍了所有的数据类型比较操作符,以及各种数据类型的语法顺序。
元组模块
这是一种创建“有状态”模块的方法。
下划线变量
这些变量会被编译器特殊对待。
第九章 类型
导出类型-export_type(...)
dialyzer 分析代码是否存在不规范和bug
第一次运行dialyzer时,需要为打算使用的所有标准库类型建立缓存。这个操作只需进行一
次。启动dialyzer后它会告诉你怎么做
λ dialyzer
输入命令会生成erts、 stdlib和kernel的PLT。PLT是Persistent Lookup Table(持久性查询表)的缩写λ dialyzer --build_plt -apps erts kernel stdlib
λ dialyzer test1.erl
第十章 编译和运行程序
code:get_path()来找到当前的载入路径值
以下是两个最常用来操作载入路径的函数:
-spec code:add_patha(Dir) => true | {error, bad_directory}
向载入路径的开头添加一个新目录Dir。
-spec code:add_pathz(Dir) => true | {error, bad_directory}
向载入路径的末端添加一个新目录Dir。
可以调用code:all_loaded()(返回一个已加载模块的总列表)
或code:clash()来帮助调查哪里出了错
也可以用这样的命令来启动Erlang:
$ erl -pa Dir1 -pa Dir2 ... -pz Dirk1 -pz Dirk2
-pa Dir标识会把Dir添加到代码搜索路径的开头, -pz Dir则会把此目录添加到代码路径
的末端
在某些系统里,主目录的位置并不清晰,或者可能和你认为的不一样。要找出Erlang认定
的主目录位置,可以这么做
1> init:get_argument(home).
{ok,[[Dir]]}
我们经常希望能在操作系统的命令行里执行任意的Erlang函数。 -eval参数非常适合进行 快速脚本编程。 这里有一个例子: erl -eval 'io:format("Memory: ~p~n", [erlang:memory(total)]).'\ -noshell -s init stop |
可以直接在命令提示符界面里编译程序。如果只是想编译一些代码而不运行它们,这就是最
简单的方式
$ erlc h.erl
$ erl -noshell -s mod func -s init stop
-s init stop在之前的命令完成后执行init:stop()函数,从而停止系统
例子里用了两个-s ..命令。命令行里的函数数量是不受限制的。每个-s ...命令都
由一个apply语句执行,运行完毕后再执行下一个命令
一个启动hello.erl的例子
hell0.sh
#!/bin/sh
erl -noshell -pa /path\
-s hello start -s init stop
这个脚本需要一个指向hello.beam文件所在目录的绝对路径。所以虽然这个脚本能在我
的机器上工作. 加个可执行权限 $ chmod u+x hello.sh
执行 $ ./hello.sh
让hello作为escript运行
hello
#!/usr/bin/env escript
main(Args) ->
io:format("hw ~n").
10.3有个精简的编译Makefile
如果Erlang崩溃了,它会留下一个名为erl_crash.dump的文件。有一个基于Web的故障分析器可以用来分析故障转储文件。要启动这个分析
器,请输入以下命令crashdump_viewer:start().
在Unix系统里,可以用下面的方式访问手册页
erl -man mod
Unix系统默认是不安装手册页的。如果命令erl -man ...无效,就需要安装手册页。所
有的手册页都存放在一个单独的压缩包里①。这些手册页应当被解压到Erlang的安装根目
录中(通常是/usr/local/lib/erlang)
第十一章 现实世界中的并发
Erlang程序由大量进程组成。这些进程间能相互发送消息。
这些消息也许能被其他进程收到和理解,也许不能。如果想知道某个消息是否已被对方
进程收到和理解,就必须向该进程发送一个消息并等待回复。
进程可以成对相互连接。如果某一对互连进程的其中一个挂了,另一个进程就会收到一
个说明前者死亡原因的消息
第十二章 并发编程
在Erlang里:
创建和销毁进程是非常快速的;
在进程间发送消息是非常快速的;
进程在所有操作系统上都具有相同的行为方式;
可以拥有大量进程;
进程不共享任何内存,是完全独立的;
进程唯一的交互方式就是消息传递
Pid = spawn(Mod, Func, Args)
创建一个新的并发进程来执行apply(Mod, Func, Args)
Pid = spawn(Fun)
创建一个新的并发进程来执行Fun()。这种形式的spawn总是使用被执行fun的当前值,而
且这个fun无需从模块里导出
Pid ! Message
向标识符为Pid的进程发送消息Message。消息发送是异步的。发送方并不等待,而是会
继续之前的工作。 !被称为发送操作符。
Pid ! M被定义为M。因此, Pid1 ! Pid2 !...! Msg的意思是把消息Msg发送给Pid1、
Pid2等所有进程
内置函数erlang:system_info(process_limit)来找出所允许的最大进程数量
系统内设的限制值是262 144个进程。要超越这一限制,必须用+P标识启动Erlang仿真器如下
erl +P max_num
函数stimer:start(Time, Fun)会在Time毫秒之后执行Fun(一个不带参数的函数)。它返
回一个句柄(是一个PID),可以在需要时用来关闭定时器
关闭定时器
注册进程
erlang:register(atom, Pid).
移除注册
erlang:unregister(atom,).
找到别名进程
erlang:whereis(atom).
返回一个包含系统里所有注册进程的列表
erlang:registered().
第十三章 并发程序中的错误
进程有两种: 普通进程和系统进程。 spawn创建的是普通进程。普通进程可以通过执行内
置函数process_flag(trap_exit, true)变成系统进程。
连接
进程可以互相连接。如果A和B两个进程有连接,而A出于某种原因终止了,就会向B发送
一个错误信号,反之亦然。
连接组
进程P的连接组是指与P相连的一组进程。
监视
监视和连接很相似,但它是单向的。如果A监视B,而B出于某种原因终止了,就会向A发
送一个“宕机”消息,但反过来就不行了。
消息和错误信号
进程协作的方式是交换消息或错误信号。消息是通过基本函数send发送的,错误信号则
是进程崩溃或进程终止时自动发送的。错误信号会发送给终止进程的连接组。
错误信号的接收
当系统进程收到错误信号时,该信号会被转换成{'EXIT', Pid, Why}形式的消息。 Pid
是终止进程的标识, Why是终止原因(有时候被称为退出原因)。如果进程是无错误终止,
Why就会是原子normal,否则Why会是错误的描述。
当普通进程收到错误信号时,如果退出原因不是normal,该进程就会终止。当它终止时,
同样会向它的连接组广播一个退出信号。
显式错误信号
任何执行exit(Why)的进程都会终止(如果代码不是在catch或try的范围内执行的话),
并向它的连接组广播一个带有原因Why的退出信号。
进程可以通过执行exit(Pid, Why)来发送一个“虚假”的错误信号。在这种情况下, Pid会
收到一个带有原因Why的退出信号。调用exit/2的进程则不会终止(这是有意如此的)。
不可捕捉的退出信号
系统进程收到摧毁信号(kill signal)时会终止。摧毁信号是通过调用exit(Pid, kill)
生成的。这种信号会绕过常规的错误信号处理机制,不会被转换成消息。摧毁信号只应该
用在其他错误处理机制无法终止的顽固进程上
基本错误处理函数
下列基本函数被用来操作连接和监视,以及捕捉和发送退出信号:
spawn_link(Fun)和spawn_link(Mod,Func,Args)
它们的行为类似于spawn(Fun)和spawn(Mod,Func,Args),同时还会在父子进程之间创
建连接。
spawn_monitor(Fun) -> {Pid, Ref}
spawn_monitor(Mod,Func,Args) -> {Pid, Ref}
它与spawn_link相似,但创建的是监视而非连接。 Pid是新创建进程的进程标识符, Ref
是该进程的引用。如果这个进程因为Why的原因终止了,消息
{'DOWN',Ref,process,Pid,Why}就会被发往父进程。
process_flag(trap_exit, true)
它会把当前进程转变成系统进程。系统进程是一种能接收和处理错误信号的进程。
link(Pid) -> true
它会创建一个与进程Pid的连接。连接是双向的。如果进程A执行了link(B),就会与B相
连。实际效果就和B执行link(A)一样。
如果进程Pid不存在,就会抛出一个noproc退出异常。
如果执行link(B)时A已经连接了B(或者相反),这个调用就会被忽略。
unlink(Pid) -> true
它会移除当前进程和进程Pid之间的所有连接。
erlang:monitor(process, Item) -> Ref
它会设立一个监视。 Item可以是进程的Pid,也可以是它的注册名称。
demonitor(Ref) -> true
它会移除以Ref作为引用的监视。
exit(Why) -> none()
它会使当前进程因为Why的原因终止。如果执行这一语句的子句不在catch语句的范围内,
此进程就会向当前连接的所有进程广播一个带有参数Why的退出信号。它还会向所有监视
它的进程广播一个DOWN消息。
exit(Pid, Why) -> true
它会向进程Pid发送一个带有原因Why的退出信号。执行这个内置函数的进程本身不会终
止。它可以用于伪造退出信号。
可以用这些基本函数来设立互相监视的进程网络,并把它作为构建容错式软件的起点
第十四章 分布式编程
节点启动
erl -sname nodename
erl -name nodename -setcookie abc
(1) 用-name参数启动Erlang。我们在同一台机器上运行两个节点时使用了“短”(short)名
称(通过-sname标识体现)。但如果它们属于不同的网络,我们就要使用-name。
当两台机器位于同一个子网时我们也可以使用-sname。而且如果没有DNS服务, -sname就
是唯一可行的方式。
(2) 确保两个节点拥有相同的cookie。这正是启动两个节点时都使用命令行参数-setcookie
abc的原因。
net_adm:ping(Node)
要让系统准备好运行分布式Erlang,需执行以下步骤。
(1) 确保4369端口对TCP和UDP流量都开放。这个端口会被一个名为epmd的程序使用(它是
Erlang Port Mapper Daemon的缩写,即Erlang端口映射守护进程)。
(2) 选择一个或一段连续端口给分布式Erlang使用,并确保这些端口是开放的。如果这些端
口位于Min和Max之间(只想用一个端口就让Min=Max),就用以下命令启动Erlang:
erl -name nodename -setcookie cookie -kernel inet_dist_listen_min Min inet_dist_listen_max Max
标准分发套装里的两个模块能够满足大多数需求。
rpc提供了许多远程过程调用服务。
global里的函数可以用来在分布式系统里注册名称和加锁,以及维护一个全连接网络。
rpc模块里最重要的函数就是下面这个。
它会在Node上执行apply(Mod, Function, Args),然后返回结果Result,如果调用失败
则返回{badrpc, Reason}。
以下是编写分布式程序的基本函数(关于这些内置函数的更完整的介绍请参见erlang模块
的手册页①)。
spawn(Node, Fun) -> Pid
它的工作方式和spawn(Fun)完全一致,只是新进程是在Node上分裂的。
spawn(Node, Mo, Func, ArgList) -> Pid
它的工作方式和spawn(Mod, Func, ArgList)完全一致,只是新进程是在Node上分裂的。
spawn(Mod, Func, Args)会创建一个执行apply(Mod, Func, Args)的新进程。它会返
回这个新进程的PID
spawn_link(Node, Fun) -> Pid
它的工作方式和spawn_link(Fun)完全一致,只是新进程是在Node上分裂的。
spawn_link(Node, Mo, Func, ArgList) -> Pid
它的工作方式类似spawn(Node, Mod, Func, ArgList),但是新进程会与当前进程相连。
disconnect_node(Node) -> bool() | ignored
它会强制断开与某个节点的连接。
monitor_node(Node, Flag) -> true
如果Flag是true就会开启监视, Flag是false就会关闭监视。如果开启了监视,那么当
Node加入或离开Erlang互连节点组时,执行这个内置函数的进程就会收到{nodeup, Node}
或{nodedown, Node}的消息。
node() -> Node
它会返回本地节点的名称。如果节点不是分布式的则会返回nonode@nohost。
node(Arg) -> Node
它会返回Arg所在的节点。 Arg可以是PID、引用或者端口。如果本地节点不是分布式的,
则会返回nonode@nohost。
nodes() -> [Node]
它会返回一个列表,内含网络里其他所有与我们相连的节点。
is_alive() -> bool()
如果本地节点是活动的,并且可以成为分布式系统的一部分, 就返回true, 否则返回false。
另外,send可以用来向一组分布式Erlang节点里的某个本地注册进程发送消息。下面的语法:
可以把消息Msg发送给节点Node上的注册进程RegName。
file:get_cwd()返回文件服务器的当前工作目录,
file:list_dir(Dir)返回Dir里所有文件的列表, file:read_file(File)读取文件File
为了确保cookie相同,分布式Erlang系
统里的所有节点都必须以相同的“神奇”(magic)cookie启动,或者通过执行erlang:set_cookie
把它们的cookie修改成相同的值
方法1:在文件$HOME/.erlang.cookie里存放相同的cookie
方法2:当Erlang启动时,可以用命令行参数-setcookie C来把神奇cookie设成C
方法3:内置函数erlang:set_cookie(node(), C)能把本地节点的cookie设成原子C
如果你的环境不够安全,那么方法1和3要优于方法2,因为Unix系统里的任何用户都可以
用ps命令来查看你的cookie。方法2只适用于测试
14.6.1 用lib_chan控制进程
lib_chan模块让用户能够显式控制自己的机器能分裂出哪些进程
start_server() -> true
它会在本地主机上启动一个服务器。这个服务器的行为由文件$HOME/.erlang_config/
lib_chan.conf决定
start_server(Conf) -> true
它会在本地主机上启动一个服务器。这个服务器的行为由文件Conf决定,它包含一个由
下列形式的元组所组成的列表:
{port, NNMM}
它会开始监听端口号NNNN。
{service, S, password, P, mfa, SomeMod, SomeFunc, SomeArgsS}
它会定义一个被密码P保护的服务S。如果这个服务启动了,就会通过分裂SomeMod:
SomeFunc(MM, ArgsC, SomeArgsS)创建一个进程,负责处理来自客户端的消息。
这里的MM是一个代理进程的PID,可以用来向客户端发送消息。参数ArgsC来自于
客户端的连接调用。
connect(Host, Port, S, P, ArgsC) -> {ok, Pid} | {error, Why}
尝试开启主机Host上的端口Port,然后尝试激活被密码P保护的服务S。如果密码正确,
就会返回{ok, Pid}。 Pid是一个代理进程的标识符,可以用来向服务器发送消息。
当客户端调用connect/5建立连接后,就会分裂出两个代理进程,一个在客户端,另一个在
服务器端。这些代理进程负责把Erlang消息转换成TCP包数据,捕捉来自控制进程的退出信号,
以及套接字关闭
第四部分 编程库与框架
第十五章 接口技术
请注意使用端口与外部进程通信和使用套接字的区别。如果使用端口,它会表现得像一个
Erlang进程,这样就可以链接它,从某个远程分布式Erlang节点向它发送消息,等等。如果使用
套接字,就不会表现出类似进程的行为。
创建端口的进程被称为该端口的相连进程。相连进程有其特殊的重要性:所有发往端口的消
息都必须标明相连进程的PID,所有来自外部程序的消息都会发往相连进程
要创建一个端口,就会调用open_port,它的说明如下
open_port(PortName,, [Opt]) -> Port
Port ! {PidC, {command, Data}}
向端口发送Data(一个I/O列表)。
Port ! {PidC, {connect, Pid1}}
把相连进程的PID从PidC改为Pid1。
Port ! {PidC, {PidC, close}}
关闭端口。
相连进程可以用以下方式从外部程序接收消息
想要在Erlang里调用一个shell脚本。要做到这一点,可以使用库函数os:cmd(Str)。
E:\erl\ports>erl
Eshell V9.3 (abort with ^G)
1> os:cmd("ipconfig").
第十六章 文件编程
用于文件操作的函数被归为四个模块。
file
它包含打开、关闭、读取和写入文件的方法,还有列出目录,等等。表7简要展示了一些
最常用的file函数。要了解所有的细节,请参阅file模块的手册页。
filename
这个模块里的方法能够以跨平台的方式操作文件名,这样就能在许多不同的操作系统上
运行相同的代码了。
filelib
这个模块是file的扩展。它包含的许多工具函数能够列出文件、检查文件类型,等等。
其中大多数都是使用file里的函数编写的。
io
这个模块有一些操作已打开文件的方法。它包含的方法能够解析文件里的数据,或者把
格式化数据写入文件
函 数 描 述
change_group 修改某个文件所属的组
change_owner 修改某个文件的所有者
change_time 修改某个文件的最后修改或访问时间
close 关闭某个文件
consult 从某个文件里读取Erlang数据类型
copy 复制文件内容
del_dir 删除某个目录
delete 删除某个文件
eval 执行某个文件里的Erlang表达式
format_error 返回一个描述错误原因的字符串
get_cwd 获取当前工作目录
list_dir 列出某个目录里的文件
make_dir 创建一个目录
make_link 创建指向某个文件的硬链接(hard link)
make_symlink 创建指向某个文件或目录的符号链接(symbolic link)
open 打开某个文件
position 设立在文件里的位置
pread 对文件里的某个位置进行读取
pwrite 对文件里的某个位置进行写入
read 对某个文件进行读取
read_file 读取整个文件
read_file_info 获取某个文件的信息
read_link 获得某个链接指向的位置
read_link_info 获取某个链接或文件的信息
rename 重命名某个文件
script 执行并返回某个文件里Erlang表达式的值
set_cwd 设置当前工作目录
sync 同步某个文件在内存和物理介质中的状态
truncate 截断某个文件
write 对某个文件进行写入
write_file 写入整个文件
write_file_info 修改某个文件的信息
第十七章 套接字编程
-module(socket_example).
-export([nano_get_url/0]).
nano_get_url() ->
nano_get_url("www.baidu.com").
nano_get_url(Host) ->
{ok, Socket} = gen_tcp:connect(Host, 80, [binary, {packet, 0}]),
ok = gen_tcp:send(Socket, "GET / HTTP/1.0\r\n\r\n"),
receive_data(Socket, []).
receive_data(Socket, SoFar) ->
receive
{tcp, Socket, Bin} ->
receive_data(Socket, [Bin|SoFar]);
{tcp_closed, Socket} ->
io:format("------------------------------------------------\n~s",
[list_to_binary(lists:reverse(SoFar))]),
list_to_binary(lists:reverse(SoFar))
end.
shell里 socket_example:nano_get_url().
顺序服务器:一次接收一个连接
并行服务器:同时接收多个并行连接
创建某个套接字(通过调用gen_tcp:accept或gen_tcp:connect)的进程被称为该套接
字的控制进程。所有来自套接字的消息都会被发送到控制进程。如果控制进程挂了,套
接 字 就 会被关 闭 。 某个套 接 字 的控制 进 程 可以通 过 调 用 gen_tcp:controlling_
process(Socket, NewPid)修改成NewPid。
我们的并行服务器可能会创建出几千个连接,所以可以限制最大同时连接数。实现的方
法可以是维护一个计数器来统计任一时刻有多少活动连接。每当收到一个新连接时就让
计数器加1,每当一个连接结束时就让它减1。可以用它来限制系统里的同时连接总数。
接受一个连接后,显式设置必要的套接字选项是一种很好的做法,就像这样:
{ok, Socket} = gen_tcp:accept(Listen),
inet:setopts(Socket, [{packet, 4}, binary, {nodelay, true}, {active, true}]),
lop(Socket).
Erlang 的 R11B-3 版 开 始 允 许 多 个 Erlang 进 程 对 同 一 个 监 听 套 接 字 调 用 gen_tcp:
accept/1。这让编写并行服务器变得简单了,因为你可以生成一个预先分裂好的进程池,
让它们都处在gen_tcp:accept/1的等待状态
如果指定{active, true}就会创建一个主动套接字,指定{active, false}则是被动套接
字。 {active, once}创建的套接字只会主动接收一个消息,接收完之后必须重新启用才能接收
下一个消息
如果一个套接字是用被动模式打开的,控制进程就必须调用gen_tcp:recv(Socket, N)
主动消息接收(非阻塞式)
被动消息接收(阻塞式)
混合消息接收(部分阻塞式)
套接字错误处理,服务器支配的套接字就会被自动关闭,同时向客户端发送一个{tcp_closed, Socket}
消息。
17.4 UDP
not used ignore
第十八章 用WebSocket 和 Erlang进行浏览
not used ignore
第十九章 用ETS和DETS存储数据
常用,翻文档就行
第二十章 Mnesia: Erlang存储数据
not used ignore
第二十一章 性能分析、调试与跟踪
标准Erlang分发套装自带了三种性能分析工具。
cprof统计各个函数被调用的次数。它是一个轻量级的性能分析器,在活动系统上运行它
会增加5%~10%的系统负载。
fprof显示调用和被调用函数的时间,结果会输出到一个文件。它适用于实验室或模拟系
统里的大型系统性能分析,并会显著增加系统负载。
eprof测量Erlang程序是如何使用时间的。它是fprof的前身,适用于小规模的性能分析
cprof:start(). 启动性能分析器
shout:start(). 运行应用程序
cprof:pause(). 暂停性能分析器
cprof:analyse(shout). 分析函数调用
cprof:stop(). 停止性能分析器
cover:start(). 启动覆盖分析器
cover:compile(shout). 编译shout.erl来进行覆盖分析
cover:analyse_to_file(shout). 分析结果
io:format调试
转储至文件,如果感兴趣的数据结构很大,就可以把它写入一个文件
使用错误记录器
Erlang 调试器
c(mod, [debug_info]).
im().
ii(mod).
iaa([init]).
im()
启动一个新的图形监视器,它是调试器的主窗口,会显示调试器正在监视的所有进程的状态。
ii(Mod)
解释Mod模块里的代码。
iaa([init])
在执行已解释代码的进程启动时让调试器关联它。
要了解更多关于调试的信息,可以试试这些资源。
http://www.erlang.org/doc/apps/debugger/debugger.pdf
这份调试器参考手册对调试器进行了介绍,包括屏幕截图和API文档,等等。它是调试器
高级用户的必读文档。
http://www.erlang.org/doc/man/i.html
在这里能找到shell里可用的调试器命令
跟踪消息与进程执行
erlang:trace/3的作用大致是,“我想要监
视这个进程,所以请在发生有意思的事时给我发一个消息”。 erlang:trace_pattern则定义了
哪些算是“有意思”的事。
第二十二章 OTP介绍
我们的回调模块必须导出六个回调方法
init/1、 handle_call/3、 handle_cast/2、
handle_info/2、 terminate/2和code_change/3
调用gen_server:start_link(Name, CallBackMod, StartArgs, Opts)来启动服务器,
之后第一个被调用的回调模块方法是Mod:init(StartArgs),它必须返回{ok, State}。 State
的值作为handle_call的第三个参数重新出现
请注意我们是如何停止服务器的。 handle_call(stop, From, Tab)返回{stop, normal,
stopped, Tab},它会停止服务器。第二个参数(normal)被用作my_bank:terminate/2的首
个参数。
服务器会因为许多原因而终止。某个以handle_开头的函数也许会返回一个{stop, Reason,
NewState},服务器也可能崩溃并生成{'EXIT', reason}。在所有这些情况下,无论它们是怎
样发生的,都会调用terminate(Reason, NewState)
第二十三章 用OTP构建系统
对程序员来说,这个错误记录器的API很简单。以下是一个简单的API子集:
error_logger:error_msg(String) -> ok
向错误记录器发送一个错误消息
error_logger:error_msg(Format, Data) % error_logger:error_msg("~s\n", "sdfd")
向错误记录器发送一个错误消息。它的参数和io:format(Format, Data)相同
error_logger:error_report(Report) -> ok
向错误记录器发送一个标准错误报告
$ erl -boot start_clean
它会创建一个适合进行程序开发的环境,只提供一种简单的错误记录形式。(不带启动参
数的erl命令就等于erl -boot start_clean。)
$ erl -boot start_sasl
它会创建一个适合运行生产系统的环境。系统架构支持库(System Architecture Support
Libraries,简称SASL)将负责错误记录和过载保护等工作
根据配置文件来启动
elog1.config
[{sasl, [
{sasl_error_logger, false}
% 写入文件 {sasl_error_logger, {file, "/path"}}
% 其他选项再看
]}].
$ erl -boot start_sasl -config elog1
分析错误
阅读错误日志是rb模块的责任,它的接口极其简单
rb:help().
rb:start().
rb:start([{max, 20}]).
rb:list().
rb:show(8). %显示最近8条
gen_event 警报关理
error:_logger:error_msg("sfddas").
23.5 监控树
启动应用程序
23.9 应用程序监视器
应用程序监视器是一个用来查看应用程序的GUI。 appmon:start()
第二十三章 用OTP构建系统
application:loaded_applications()
application.load(:atom_name)
application.start(:atom_name)
application.stop(:atom_name)
application.unload(:atom_name)
应用程序监视器 appmon:start()
第五部分 构建应用程序
第二十五章 编程术语
安装rebar
rebar可以在https://github.com/basho/rebar里找到。你应该能在https://github.com/rebar/rebar/
wiki/rebar找到rebar的预编译二进制文件。要安装rebar,请复制该文件,修改文件模式为可执行,
然后把它放在路径里的某个地方。
做完这些之后,应当测试一下是否能运行rebar
$ rebar -V
rebar create-app apped=bertie命令创建了标准OTP应用程序所需的样板文件和目录结构
rebar get-deps获取这些依赖项,命令从GitHub获取bitcask并把它保存在名为deps的子目录里
用rebar compile命令来编译它们
cowboy 是 一 个 小 型 、 快 速 和 模 块 化 的 HTTP 服 务 器 , 它 是 用 Erlang 编 写 的 , 可 以 在
https://github.com/extend/cowboy里找到。它得到了Nine Nines①公司的支持
第二十六章 多核CPU编程