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分布式编程的更多相关文章

  1. Erlang入门(三)——分布式编程

    明天要回家一个星期了,好好休息下.今天找到别人翻译的Erlang编程手册,值的好好读一遍.    所谓分布式的Erlang应用是运行在一系列Erlang节点组成的网络之上.这样的系统的性质与单一节点上 ...

  2. erlang分布式编程模型

    erlang分布式编程有两种模型 一.分布式erlang 运行在可信的网络环境中 1.rpc提供的远程过程调用 rpc:call(Node,Mode,Fun,Args) ->Result|{ba ...

  3. 《erlang程序设计》学习笔记-第3章 分布式编程

    http://blog.csdn.net/karl_max/article/details/3985382 1. erlang分布式编程的基本模型 (1) 分布式erlang:这种模型可以让我们在一个 ...

  4. Erlang 101 Erlang环境和顺序编程

    笔记系列 Erlang环境和顺序编程 Erlang并发编程 Erlang分布式编程 Yaws Erlang/OTP 日期              变更说明2014-10-12 A outline, ...

  5. Erlang 102 Erlang并发编程

    笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期              变更说明 2014-11-02 A outline 2014 ...

  6. [Erlang 0124] Erlang Unicode 两三事 - 补遗

    最近看了Erlang User Conference 2013上patrik分享的BRING UNICODE TO ERLANG!视频,这个分享很好的梳理了Erlang Unicode相关的问题,基本 ...

  7. [Erlang 0105] Erlang Resources 小站 2013年1月~6月资讯合集

    很多事情要做,一件一件来; Erlang Resources 小站 2013年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/     ...

  8. [Erlang 0129] Erlang 杂记 VI

    把之前阅读资料的时候记下的东西,整理了一下. Adding special-purpose processor support to the Erlang VM   P23 简单介绍了Erlang C ...

  9. [Erlang 0122] Erlang Resources 2014年1月~6月资讯合集

    虽然忙,有些事还是要抽时间做; Erlang Resources 小站 2014年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/   ...

随机推荐

  1. ShareSDK第三方登录代码

    - (IBAction)YYSJBut:(UIButton *)sender{    if (sender.tag == 7)    {        [self AuthLogin:SSDKPlat ...

  2. 一个webpack,react,less,es6的DEMO

    1.package.json如下 { "name": "demo", "version": "1.0.0", " ...

  3. 《BI项目笔记》多维数据集中度量值设计时的聚合函数

    Microsoft SQL Server Analysis Services 提供了几种函数,用来针对包含在度量值组中的维度聚合度量值.默认情况下,度量值按每个维度进行求和.但是,通过 Aggrega ...

  4. Android Studio 简单设置

    转自:http://ask.android-studio.org/?/article/14 界面设置 默认的 Android Studio 为灰色界面,可以选择使用炫酷的黑色界面.Settings - ...

  5. openstack Icehouse发布

    OpenStack 2014.1 (Icehouse) Release Notes General Upgrade Notes Windows packagers should use pbr 0.8 ...

  6. ElasticSearch学习问题记录——Invalid shift value in prefixCoded bytes (is encoded value really an INT?)

    最近在做一个电商项目,其中商品搜索中出现一个奇怪的现象,根据某个字段排序的时候会出现商品数量减少的情况.按照一般路要么查不出来,要么正常显示,为什么增加了按照销量排序就会出现查询结果减少的情况. 查了 ...

  7. python3 对文件的查找、替换、删除

    python 版本 3.5 实现对文件的查找,替换,删除 #Author by Andy #_*_ coding:utf-8 _*_ #定义查找函数 def find(): Keywords=inpu ...

  8. 【apache】yum 安装Apache(Centos 6.5)

    一.首先在系统上面查询一下是否已经安装了apache [Apache在linux系统里的名字是httpd] rpm    -qa    httpd 如果有返回的信息,则会显示已经安装的软件.如果没有则 ...

  9. C#高级进阶--重写函数

    这里要说一下,重写是继承时发生,在子类中重新定义父类中的方法.例如:基类方法声明为virtual方法,派生类中使用override声明此方法的重写. 基类中的访问修饰符在子类中是不能被修改的.比如说基 ...

  10. js输入,输出基本操作