Erlang 103 Erlang分布式编程
Outline
笔记系列
Erlang环境和顺序编程
Erlang并发编程
Erlang分布式编程
Yaws
Erlang/OTP
日期 变更说明
2014-11-23 A Outline
A 1.1-1.2
2014-12-08 A 1.3
2014-12-13 A 2, 3
2014-12-14 A 4
Agenda

写在前面
Erlang研磨和技术细节,可以移步园中http://www.cnblogs.com/me-sa/。如果一开始我就可以看到他的笔记的话,...。
0范围
节点和通信
基本分布式编程模块
empd进程
套接字编程
1 Erlang节点和通信
1.1节点
一个Erlang节点是已命名的(named)的正在运行的Erlang运行时系统(erts)。
多个节点可以运行在一台机器上,也可以运行在不同的机器上。之前的顺序编程、并发编程中示例实际上是在一个Erlang节点上运行的。
存活节点/可命名的节点:如果一个节点可以与其他节点通信。任何存活的节点都需要命名,命名有两种方式:
(1)短名字 erl -sname <sname>
sname在局域网中命名一个主机,已name@host给出,例:foo@zhoujiagen
(2)长名字 erl -name <name>
name给出完整的主机IP地址,可以是foo@192.168.1.103,或foo@zhoujiagen(可DNS解析的主机名)。
长名字节点只能与长名字节点通信,短名字节点只能与短名字节点通信。
节点启动和信息
erl -[s]name <nodeName>
命名节点方式启动
net_kernel:start([‘foo@zhoujiagen’]).
启动节点
net_kernel:stop().
停止当前节点
node().
查看当前节点
elrang:is_alive().
当前节点是否存活
1.2 节点通信
节点连通性测试
net_adm:ping(‘bar@zhoujiagen’).
pang:不连通,pong:连通。
Cookie
每个节点在任意时刻只有一个cookie,共享同一值的节点可以通信。
启动时设置cookie
erl -sname foo -setcookie <cookieValue>
运行时设置cookie
erlang:set_cookie(node(), <cookieValue>).
局限性
分布式节点通过cookie与另一远程节点建立连接后,远程节点的拥有者获得本地节点运行者用户相同的权限。远程节点能够执行spawn(YourNode, os, cmd, [“rm -rf *”]).是任何人都不想看到的。故,cookie安全通信在封闭式系统中是足够的,但在开放式系统中需要融入其他的安全通信机制,可以考虑安全套接字等。
连接性建立和配置
只要分布式Erlang节点共享相同的cookie值,它们之间就可以通信。
Erlang运行时系统(erts)在第一次引用一个节点时,自动建立连接。自动建立连接可以是通过调用net_adm:ping/1或者发送一个消息到该节点注册的进程上来完成。
连接在一起的节点,信息是共享的,具有可传递性。
net_kernel进程
每个节点的net_kernel进程负责协调分布式Erlang节点之间的操作,例如:spawn/4会被net_kernel进程转换为消息发送到远程节点的net_kernel进程上、net_kernel进程处理cookie认证。很重要的一点:用户可以修改net_kernel进程以获得期望的行为。
自动建立的可传递的连接行为的覆盖方法有:使用net_kernel模块中的函数手动的控制连接、运行erl -connect_all false拒绝节点全局的相互连接。
隐藏节点
希望覆盖默认配置下所有节点互相连接行为,只在必要时建立与其他节点的连接的时候,可以考虑隐藏节点。
启动隐藏节点: erl -sname ‘nodeName’ -hidden。
手动建立节点连接:net_kernel:connect(NodeName).。
nodes/0不会返回隐藏节点,nodes(hidden)返回隐藏节点,nodes(connected)返回隐藏节点和非隐藏节点。
1.3 远程过程调用与本地调用的区别
►Sample: facserver/facclient[1]
facserver.erl
-module(facserver).
%% API
-export([start/0]).
%%%===================================================================
%%% API
%%%===================================================================
start() ->
register(facserver, self()),
facLoop().
%%%===================================================================
%%% Internal functions
%%%===================================================================
facLoop() ->
receive
{Pid, N} ->
Pid ! {ok, fac(N)}
end,
facLoop().
fac(0) -> 1;
fac(1) -> 1;
fac(N) -> N * fac(N-1).
facclient.erl
-module(facclient).
%% API
-export([remote_call/2]).
%%%===================================================================
%%% API
%%%===================================================================
remote_call(Message, Node) ->
{facserver, Node} ! {self(), Message}, %◄向远程节点远程发送消息节点
receive
{ok, Result} ->
Result
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================
运行示例


分布式系统是一个会因为另一个你甚至不知道其存在的计算机的错误,而导致你的计算机无法使用的系统。
—— Lesilie Lamport
远程过程调用与本地调用的本质区别在于:远程节点可能失效,无法调用期望的功能。故,需要在远程过程调用时添加一些“护卫”语句,以确保远程节点连接正常、远程节点失效时有机会优雅的继续处理。[1]中给出了三种常见的方法:超时(receive … after)、连接到远程节点进程(spawn_link/4)、监视远程节点进程(monitor_node(Node, Bool));有关细节可以参考Erlang随版本发布的文档。
2 基本分布式编程模块
2.1 erl模块
包括erl命令,用于启动Erlang运行时系统(erts),一些启动标志可以改变ert的行为:
-connect_all false
系统不维护连接节点的全局列表,即阻止全局声明。
-hidden
启动一个隐藏节点。
-name Name / -sname Name
设置节点名称。
-setcookie Cookie
设置节点的cookie。
2.2 rpc模块
rpc模块包含类似于远程过程调用的服务、广播和并行计算等功能。远程过程调用用于收集远程节点上的信息、或在远程节点上执行一些有副作用的功能。
call(Node, Module, Function, Args [, Timeout]) -> Res | {badrpc, Reason}
在节点Node上执行apply(Module, Function, Args),带超时的call/5会spawn出临时进程接收超时的回复。
block_call(Node, Module, Function, Args [, Timeout]) -> Res | {badrpc, Reason}
类似与call,但节点Node上RPC server不会创建独立的进程处理请求。
async_call()Node, Module, Function, Args) -> Key
实现流式调用承诺,调用方不会阻塞。返回的Key可以视为回复的承诺,后续用yield/1或nb_yield/1,2获取Res。
2.3 erlang模块
Erlang的BIF集合,涉及分布式编程的函数有:
erlang:set_cookie(Node, Cookie)
设置节点的cookie值。
erlang:get_cookie()
返回当前节点的cookie值,当前节点不活时返回nocookie。
node()
返回当前节点的Name@Host,不活时返回nonode@nohost。
node(Arg)
返回Arg所在的节点,Arg可以是pid、reference或port。
注:port的概念参见” Erlang Reference Manual User's Guide” §14,简单的说,port是Erlang与外部世界通信机制抽象。
nodes()
返回系统中可见的其他节点。
spawn(Node, Module, Function, ArgumentList)
在节点Node上执行spawn(Module, Function, ArgumentList)。
disconnect_node(Node)
断开与节点Node的连接。
monitor_node(Node, Flag)
打开或关闭对节点Node的监控。
2.4 net_kernel模块
net_kernel模块包含对手动起停、连接和监控节点的构造。默认情况下由erts自动调用,但用户可以通过修改来获得预期的行为。
例:connect_node(Node) -> boolean() | ignored
建立与节点Node的连接。
至于如何修改配置覆盖默认行为,待对erts有深入了解后再做探讨。
2.5 net_adm模块
Erlang的网络管理模块,包括ping等。
例:ping(Node) -> pong | pang
尝试建立与节点Node的连接,失败返回pang,成功返回pong。
3 epmd进程
epmd守护进程在参与分布式Erlang计算的主机上充当名称服务器,每个Erlang节点启动时,会获得一个名称和一个主机OS内核中的地址,在TCP/IP环境中,该地址是ip:port。节点将获得的名称和地址发送给本机上的empd守护进程。epmd负责将符号节点名称映射到地址上。
epmd监听的默认端口是4369。
详细内容参见” Erlang Run-Time System Application (ERTS) Reference Manual ” COMMAND empd。
4 Erlang套接字编程
操作系统视角的套接字(socket)阐述参见[3],这里不再班门弄斧,只做必要的说明。
值得特别说明的是下面的示例依赖于类似于UNIX DOMAIN Socket的机制,即单个主机内部应用程序之间通过Socket通信。
4.1 UDP
这里只提供一个交互示例,来源于[1],为体现Socket与节点通信的异同,UDP对偶端分别运行在两个本地节点上。
(1)首先启动两个本地节点、查看节点连通性


(2) 在foo节点上开启1234 UDP监听端口,在bar节点上开启1235 UDP监听端口


netstat一下

(3) 尝试发送一些消息/数据,再查看一下节点连通性


对偶端也来一次


(4) 关闭UDP Socket,inet:i()用于列举套接字


(5) 现在看看节点连通时发生了什么


原来也是用Socket通信的。
4.2 TCP
示例来源于[1],TCP server、client之间简单的通信。
►Sample: tcp_demo [1]
-module(tcp_demo).
%% API
-export([client/2, server/0, wait_connect/2]).
%%%===================================================================
%%% API
%%%===================================================================
client(Host, Data) ->
{ok, Socket} = gen_tcp:connect(Host, 1234, [binary, {packet, 0}]),
send(Socket, Data),
ok = gen_tcp:close(Socket).
server() ->
{ok, ListenSocket} = gen_tcp:listen(1234, [binary, {active, false}]),
wait_connect(ListenSocket, 0).
wait_connect(ListenSocket, Count) ->
{ok, Socket} = gen_tcp:accept(ListenSocket), % 成为控制进程
spawn(?MODULE, wait_connect, [ListenSocket, Count+1]), % 生成新的监听进程
get_request(Socket, [], Count).
%%%===================================================================
%%% Internal functions
%%%===================================================================
send(Socket, <<Chunk:100/binary, Rest/binary>>) ->
gen_tcp:send(Socket, Chunk),
send(Socket, Rest);
send(Socket, Rest) ->
gen_tcp:send(Socket, Rest).
get_request(Socket, BinaryList, Count) ->
case gen_tcp:recv(Socket, 0, 5000) of
{ok, Binary} ->
get_request(Socket, [Binary | BinaryList], Count);
{error, closed} ->
handle(lists:reverse(BinaryList), Count)
end.
handle(Binary, Count) ->
{ok, Fd} = file:open("log_file.log", [write, append]),
file:write(Fd, "Incoming " ++ integer_to_list(Count) ++ ":\n"),
file:write(Fd, Binary ++ "\n\n"),
file:close(Fd).
执行
(1)启动server

(2)使用client发送数据

(3)查看端口

(4)查看输出文件

(5)关闭客户端后查看端口

4.3 常见模块说明
4.3.1 gen_udp模块
UDP库模块,有关选项参数这里不列出,可以参考各模块的文档。
打开socket
gen_udp:open(Port) -> {ok, Socket} | {error, Reason}
gen_udp:open(Port, OptionList) -> {ok, Socket} | {error, Reason}
发送数据
gen_udp:send(Socket, Address, Port, Packet) -> ok | {error, Reason}
接收数据
gen_udp:recv(Socket, Length) -> {ok, {Address, Port, Packet}} | {error, Reason}
gen_udp:recv(Socket, Length, Timeout) -> {ok, {Address, Port, Packet}} | {error, Reason}
管理socket
gen_udp:close(Socket) -> ok
4.3.2 gen_tcp模块
TCP库模块。
启动监听socket
gen_tcp:listen(PortNumber, Options)
开始监听
gen_tcp:accept(Socket)
gen_tcp:accept(Socket, Timeout)
请求连接
gen_tcp:connect(Address, Port, OptionList) -> {ok, Packet} | {error, Reason}
gen_tcp:connect(Address, Port, OptionList, Timeout) -> {ok, Packet} | {error, Reason}
发送数据
gen_tcp:send(Socket, Packet) -> ok | {error, Reason}
被动模式下读取数据({active, false})
gen_tcp:recv(Socket, Length) -> {ok, Packet} | {error, Reason}
gen_tcp:recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason}
raw模式的socket,简单的说应用程序不通过传输层,直接使用IP层数据 [3]。
关闭socket
gen_tcp:close(Socket) -> ok
控制进程和控制传递
控制进程:通过gen_tcp:accept或者gen_tcp:connect建立连接的进程。
控制传递:控制进程调用gen_tcp:controlling_process(Socket, Pid) -> ok | {error, Reason}。
请求处理的两种方式:
wait_connect(ListenSocket, Count) ->
{ok, Socket} = gen_tcp:accept(ListenSocket), % 成为控制进程
spawn(?MODULE, wait_connect, [ListenSocket, Count+1]), % 生成新的监听进程
get_request(Socket, [], Count).
=>
wait_connect(ListenSocket, Count) ->
{ok, Socket} = gen_tcp:accept(ListenSocket), % 成为控制进程
Pid = spawn(?MODULE, get_request, [Socket, [], Count]), % 生成新的控制进程
gen_tcp:controlling_process(Socket, Pid),
wait_connect (ListenSocket, Count+1). % 继续等待/接收数据
4.3.3 inet模块
inet模块提供访问TCP/IP的方法,包含处理socket的一些函数。
获取和设置socket配置
inet:getopts(Socket, Options) -> {ok, OptionValues} | {error, posix()}
inte:setopts(Socket, Options) -> ok | {error, posix()}
获取socket统计数据
inte:getstat(Socket) -> {ok, OptionValues} | {error, posix()}
inte:getstat(Socket, Options) -> {ok, OptionValues} | {error, posix()}
一些有用的函数
inet:gethostname() 查看当前主机名称
inet:i() 查看已创建的所有socket信息
参考文献
[1] Cesarini F., Thompson S.著,慕尼黑Isar工作组 杨剑译.
Erlang编程指南.
北京: 机械工业出版社.2011.
[2] Armstrong J.著,牛化成 译.
Erlang程序设计(第2版).(Programming Erlang, Second Edition – Software for a Concurrent World).
北京: 人民邮电出版社.2014.
[3] Kerrisk M.著,郭光伟 陈舸 译.
Linux/UNIX系统编程手册(下册).
北京: 人民邮电出版社.2014.
Erlang 103 Erlang分布式编程的更多相关文章
- Erlang入门(三)——分布式编程
明天要回家一个星期了,好好休息下.今天找到别人翻译的Erlang编程手册,值的好好读一遍. 所谓分布式的Erlang应用是运行在一系列Erlang节点组成的网络之上.这样的系统的性质与单一节点上 ...
- erlang分布式编程模型
erlang分布式编程有两种模型 一.分布式erlang 运行在可信的网络环境中 1.rpc提供的远程过程调用 rpc:call(Node,Mode,Fun,Args) ->Result|{ba ...
- 《erlang程序设计》学习笔记-第3章 分布式编程
http://blog.csdn.net/karl_max/article/details/3985382 1. erlang分布式编程的基本模型 (1) 分布式erlang:这种模型可以让我们在一个 ...
- Erlang 101 Erlang环境和顺序编程
笔记系列 Erlang环境和顺序编程 Erlang并发编程 Erlang分布式编程 Yaws Erlang/OTP 日期 变更说明2014-10-12 A outline, ...
- Erlang 102 Erlang并发编程
笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期 变更说明 2014-11-02 A outline 2014 ...
- [Erlang 0124] Erlang Unicode 两三事 - 补遗
最近看了Erlang User Conference 2013上patrik分享的BRING UNICODE TO ERLANG!视频,这个分享很好的梳理了Erlang Unicode相关的问题,基本 ...
- [Erlang 0105] Erlang Resources 小站 2013年1月~6月资讯合集
很多事情要做,一件一件来; Erlang Resources 小站 2013年1月~6月资讯合集,方便检索. 小站地址: http://site.douban.com/204209/ ...
- [Erlang 0129] Erlang 杂记 VI
把之前阅读资料的时候记下的东西,整理了一下. Adding special-purpose processor support to the Erlang VM P23 简单介绍了Erlang C ...
- [Erlang 0122] Erlang Resources 2014年1月~6月资讯合集
虽然忙,有些事还是要抽时间做; Erlang Resources 小站 2014年1月~6月资讯合集,方便检索. 小站地址: http://site.douban.com/204209/ ...
随机推荐
- Linux命令基本格式及目录处理命令
命令提示符 [root@localhost ~]# root:当前登录用户 localhost:主机名 ~:当前所在的目录,此处为"家"目录 #:root超级用户的提示符,如果是普 ...
- 通过HWND获得CWnd指针
cwnd 又为计算机网络中拥塞窗口(congestion window)的简写.拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化.发送方让自己的发送窗口还可能小于拥塞窗口. CWnd是MFC窗口类 ...
- Sprint(第六天11.19)
燃尽图
- 3个常用基于Linux系统命令行WEB网站浏览工具(w3m/Links/Lynx)
一般我们常用的浏览器肯定是基于可视化界面的图文结合的浏览界面效果,比如FireFox.Chrome.Opera等等,但是有些时候折腾和项目 的需要,在Linux环境中需要查看某个页面的文字字符,我们需 ...
- Response.End()在Webform和ASP.NET MVC下的表现差异
前几天在博问中看到一个问题--Response.End()后,是否停止执行?MVC与WebForm不一致.看到LZ的描述后,虽然奇怪于为何用Response.End()而不用return方式去控制流程 ...
- treeview递归加载
实体类: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ...
- 学习mongo系列(九)索引,聚合,复制(副本集),分片
一.索引 二.聚合 三.复制(副本集) 四.分片 尚未实践操作. 详见http://www.runoob.com/mongodb/mongodb-indexing.html
- android,NDK android.mk相关
1.c++ try...catch的支持 需要在Android.mk 中添加 LOCAL_CPPFLAGS += -fexceptions,或者在Application.mk中添加APP_CPPFLA ...
- php短路与 短路或
关于php短路的问题特性,三种写法的区别 $a = 1;$b=0;第一种: $a && $b = 'cccccccc';第二种 $a || $b = 'cccccccc';第三种 if ...
- ubuntu忘记密码怎么办
刚安装了,ubuntu14.04,就想着,如果忘记登录密码,这可不好办,所以测试下开机,刚过bios显示画面,不停的点击,,键盘左边的shift键.(因为刚开始是采用按着不放的办法,结果不灵.所以我不 ...