Erlang下与其他程序和语言的通信机制(3)
这部分主要聊C Nodes。首先我们需要了解下Erlang的分布式系统:
分布式Erlang:
分布式Erlang是由一组相互通信的Erlang运行时组成,其中每一个运行时称为一个Node。不同Node上的进程通过消息通信(TCP/IP),并且Node对于进程来说是透明的。比如Pid ! Msg,调用进程不需要知道Pid是在哪个Node上。但是当使用进程的注册名进行消息投递时,如果投递的进程不在同一个Node上,这时我们需要带上Node的名称才行,因为进程注册名只是进程所属Node上的本地信息。
【启动Node】
一个Node通过添加命令行-name 或者 -sname启动运行时来创建,创建好的Node名称为name@host格式。其中name是Node的名称标示,类似于端口号,host为Node所存在的主机名。当使用-name启动表示使用长名,也就是说host部分为完整的主机名字。当使用-sname启动表示使用短名,host部分只是主机名的第一部分。长名一般用于配有DNS的网络环境,短名则适用于DNS不可用的环境,只需要所有Node处于同一个子网。长名Node与短名Node之间是不能通信的。
% erl -name dilbert
(dilbert@uab.ericsson.se)1> node().
'dilbert@uab.ericsson.se' %%在ubuntu13.04下调用会失败。需要手动添加主机名。
% erl -name dilbert@host % erl -sname dilbert
(dilbert@uab)1> node().
dilbert@uab
【Node连接】
当Node第一次调用函数与其他Node通信时,这个Node将会与这个Node建立连接,比如调用spawn(Node,M,F,A),或者net_adm:ping(Node)。默认下,连接是具有传递性的,比如说Node A连接到Node B,而Node B之前已经连接到了Node C,那么Node A也会试图连接Node C。如果一个Node关闭,所有与这个Node相关的连接会相应关闭。这个特性可以通过启动时添加-connect_all false来关闭,其实这个特性是由global模块提供的,关闭了这个特性后,global模块也将不提供对Pid注册全局名称的服务。
【global模块】
global模块由global_name_server进程构成,这个进程存在于每个Node上,在Node启动时,自动启动(kernel app下)。它主要提供以下3中服务:
- 为Pid提供全局名称注册,类似于本地注册,注册后可以调用global:send为远程Node的Pid发送消息。如果注册的Pid终止了,注册的名字将自动消除。Erlang在实现这个服务时,并没有提供中心的服务进程来保存全局的注册名与Pid对应表,而是在每个Node上各自保存一份数据,因此Name与Pid的转换在本地计算,速度很快。但一旦全局表里的数据发生了变化,那么所有连接的Node都需要更新数据。当然这个过程是由Erlang帮你完成的。
- 提供全局锁服务。Pid可以对访问的资源进行加锁(set_lock),加锁后的资源只能被这个Pid访问,其它Pid访问将被拒绝。如果加锁的Pid终止,或者其上的Node终止,该资源的锁会自动解除。
- 维护全连接服务。上面说到Node的互相连接就是由这个服务提供的。
【epmd】
epmd全称Erlang Port Mapper Daemon,主要用于Node名称与机器地址的关联,当一个Node启动时,epmd将自动启动。它在分布式Erlang中扮演一个域名服务器的角色。当一个Node启动时将从OS Kernel中获得地址信息,然后Node将Node和名称与地址一起发送给本机上运行的epmd守护进程。在TCP/IP下,地址信息包含IP和端口。而Node名称则为一个原子(Name@Node)。这样一来,epmd就知道Node在哪个地址和端口上监听了。
【安全性】
Node之间连接的验证通过自己的magic cookie来进行判断。当一个Node试图连接另外一个Node时,首先需要对比magic cookies。如果相等,那么验证通过,反之则不然。只有通过验证的Node才能够相互连接。
当Erlang network authentication server(auth)启动时,首先会去读取$Home/.erlang.cookie文件,如果文件不存在的话,则创建一个。这个文件的访问权限是400,里面就是保存的该Host上Node的cookie。也可以通过erlang:set_cookie在代码里设置Node的cookie。
【分布式Erlang相关的模块】
C Node
在开始C Node之前,先了解下Hidden Node。
【Hidden Nodes】
在有一些情况下,我们不希望Node A连接一个Node B后,Node A继续对Node B已知的其它Node进行连接。这时可以使用-hidden将Node标示为Hidden Node,并且也不会显示在Nodes调用的列表中。这部分主要在global_group中实现。大概实现是这样,global_group模块会根据启动的Node是否为Hidden,在net_kernel里注册该Node可见的范围。比如说这个Node为hidden,no_group,那么这个Node对任何Node不可见。如果这个Node属于一个group组时,那么这个Node在hidden时只对组内可见。同样group在配置时,也可以配置为hidden,这时不管组内Node是normal还是hidden,组内的Node都只对组内可见,可以参看pushlish_on_nodes那四个函数。Node的group可以通过Kernel的config文件配置,config文件通过-config引用,建立global_group的原因主要是当Node太多时,降低不必要的Node维护开销。
publish_on_nodes(normal, no_group) ->
all;
publish_on_nodes(hidden, no_group) ->
[];
publish_on_nodes(normal, {normal, _}) ->
all;
publish_on_nodes(hidden, {_, Nodes}) ->
Nodes;
publish_on_nodes(_, {hidden, Nodes}) ->
Nodes. %%%====================================================================================
%%% Update net_kernels publication list
%%%====================================================================================
update_publish_nodes(PubArg) ->
update_publish_nodes(PubArg, no_group).
update_publish_nodes(PubArg, MyGroup) ->
net_kernel:update_publish_nodes(publish_on_nodes(PubArg, MyGroup)).
【C Node】
C Node在分布式Erlang中也是作为隐藏Node来处理的。在Erlang这边对他的访问和访问一个普通Node一样,也是直接通过{RegName,Node} ! Msg访问。如果Node使用短名,则Node按cN(N为整数)命名。如果是长名,则没有限制。比如说短命叫c1@dril,长名叫cnode@idril.ericsson.se。访问时RegName为一个原子any,它将被C忽略。
-module(complex3).
-export([foo/1, bar/1]). foo(X) ->
call_cnode({foo, X}).
bar(Y) ->
call_cnode({bar, Y}). call_cnode(Msg) ->
{any, c1@idril} ! {call, self(), Msg},
receive
{cnode, Result} ->
Result
end.
在C这边,还是先使用erl_init函数初始化。短名通过erl_connect_init,长名通过erl_connect_xinit初始化Node。如果C Node作为客户端的话,就使用erl_connect连接其他Node,如果是服务器,就需要创建监听Socket,并通过erl_publish将Node的名称与地址端口对应暴露给epmd。最后是具体的消息处理部分类似于普通端口,可以参看第一篇。
/* cnode_s.c */ #include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> #include "erl_interface.h"
#include "ei.h" #define BUFSIZE 1000 int main(int argc, char **argv) {
int port; /* Listen port number */
int listen; /* Listen socket */
int fd; /* fd to Erlang node */
ErlConnect conn; /* Connection data */ int loop = ; /* Loop flag */
int got; /* Result of receive */
unsigned char buf[BUFSIZE]; /* Buffer for incoming message */
ErlMessage emsg; /* Incoming message */ ETERM *fromp, *tuplep, *fnp, *argp, *resp;
int res; port = atoi(argv[]); erl_init(NULL, ); if (erl_connect_init(, "secretcookie", ) == -)
erl_err_quit("erl_connect_init"); /* Make a listen socket */
if ((listen = my_listen(port)) <= )
erl_err_quit("my_listen"); if (erl_publish(port) == -)
erl_err_quit("erl_publish"); if ((fd = erl_accept(listen, &conn)) == ERL_ERROR)
erl_err_quit("erl_accept");
fprintf(stderr, "Connected to %s\n\r", conn.nodename); while (loop) { got = erl_receive_msg(fd, buf, BUFSIZE, &emsg);
if (got == ERL_TICK) {
/* ignore */
} else if (got == ERL_ERROR) {
loop = ;
} else { if (emsg.type == ERL_REG_SEND) {
fromp = erl_element(, emsg.msg);
tuplep = erl_element(, emsg.msg);
fnp = erl_element(, tuplep);
argp = erl_element(, tuplep); if (strncmp(ERL_ATOM_PTR(fnp), "foo", ) == ) {
res = foo(ERL_INT_VALUE(argp));
} else if (strncmp(ERL_ATOM_PTR(fnp), "bar", ) == ) {
res = bar(ERL_INT_VALUE(argp));
} resp = erl_format("{cnode, ~i}", res);
erl_send(fd, fromp, resp); erl_free_term(emsg.from); erl_free_term(emsg.msg);
erl_free_term(fromp); erl_free_term(tuplep);
erl_free_term(fnp); erl_free_term(argp);
erl_free_term(resp);
}
}
} /* while */
} int my_listen(int port) {
int listen_fd;
struct sockaddr_in addr;
int on = ; if ((listen_fd = socket(AF_INET, SOCK_STREAM, )) < )
return (-); setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset((void*) &addr, , (size_t) sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(listen_fd, (struct sockaddr*) &addr, sizeof(addr)) < )
return (-); listen(listen_fd, );
return listen_fd;
}
恩,这个议题终于写完了,当然要深入下去还有很多可以写。不过也写得有点疲了,先告一段落吧。
Erlang下与其他程序和语言的通信机制(3)的更多相关文章
- Erlang下与其他程序和语言的通信机制(1)
在Erlang运行时中,提供了几种机制来实现与其它程序或者语言的通信.一种为分布式Erlang,一种为端口,其中端口分为普通端口和链入式驱动端口,还有后面引入的NIFs. 分布式Erlang:一个Er ...
- Erlang下与其他程序和语言的通信机制(2)
前面聊了普通端口,今天聊下链入式驱动端口,以及NIFs. 链入式驱动端口 如上图所示,链入式驱动端口与Erlang虚拟机存在于同一个OS进程中. 在Erlang这边与普通端口类似,所有与链入式驱动端口 ...
- netlink---Linux下基于socket的内核和上层通信机制 (转)
需要在linux网卡 驱动中加入一个自己的驱动,实现在内核态完成一些报文处理(这个过程可以实现一种零COPY的网络报文截获),对于复杂报文COPY下必要的数据交给用户 态来完成(因为过于复杂的报文消耗 ...
- Linux下who命令之C语言实现
Linux下who命令之C语言实现 Step1:前期准备 首先要有一个清楚的认识:linux中一切皆文件 实现who命令,who命令也是Linux中的一个文件,那我们怎么找到它呢?我们可以" ...
- JAVA设置环境变量和在DOS下运行java程序
在学校实训的这几天,老师带着我们开始深入的复习java.这是第一天的内容哦 对于“JAVA设置环境变量和在DOS下运行java程序”,许多初学者是陌生的,但了解这个却对后期的学习很重要. http:/ ...
- erlang下lists模块sort(排序)方法源码解析(一)
排序算法一直是各种语言最简单也是最复杂的算法,例如十大经典排序算法(动图演示)里面讲的那样 第一次看lists的sort方法的时候,蒙了,几百行的代码,我心想要这么复杂么(因为C语言的冒泡排序我记得不 ...
- 微信小程序开发语言的选择
微信使用的开发语言和文件很「特殊」. 小程序所使用的程序文件类型大致分为以下几种: ①WXML(WeiXin Mark Language,微信标记语言) ②WXSS(WeiXin Style Shee ...
- Linux下的 sniff-andthen-spoof程序编写
Linux下的 sniff-andthen-spoof程序编写 一.任务描述 在本任务中,您将结合嗅探和欺骗技术来实现以下嗅探然后欺骗程序.你需要两台机器在同一个局域网.从机器A ping IP_X, ...
- VB.net 2010下关联与程序图标设置
'*************************************************************************'**模 块 名:VB.net 2010下关联与程序 ...
随机推荐
- WordPress强制跳转https教程
在互联网火热的今天,安全问题显得越来越重要,为了用户信息安全,很多热门网站都启用了https 有小伙伴就问:我启用了https,为什么访问的时候显示的还是http呢? 其实,有时候并不是因为我们ssl ...
- Linux学习日记之crontab使用notify-send实现每小时通知提醒
crontab命令用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行 通过crontab -e 可以打开编辑文件添加新的命令 notif ...
- window下编写python脚本在linux下运行出错 usr/bin/python^M: bad interpreter: No such file or directory
今天在windows下使用notepad++写了个python脚本,传到linux服务器执行后提示:-bash: ./logger.py: usr/bin/python^M: bad interpre ...
- java.lang.RuntimeException: java.lang.NullPointerException...的错误
先FQ,让电脑能登上谷歌,然后重新安装,应该就好了,我的是这样解决的.如果下次安装又报:java.lang.RuntimeException: java.lang.NullPointerExcepti ...
- 2-2 列表推导同 filter 和 map 的比较
列表推导同 filter 和 map 的比较 参考廖雪峰的文档: filter()函数:用于过滤序列. filter()接收一个函数和一个序列.把传入的函数依次作用于传入的序列的每个元素,根据返回值是 ...
- POJ_2387_最短路
Til the Cows Come Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 46859 Accepted ...
- Android(java)学习笔记206:JNI之工具快速开发步骤
下面通过一个案例说明一下,利用工具jni快速开发步骤 1.新建一个Android工程,命名为"03_对int数组加1",如下: 2. 在MainActivity.java中对add ...
- UI布局术语
horizontal, vertical top, left, bottom, right UIEdgeInsets margin与padding Interpreting Values Values ...
- Xamarin View获取属性的绑定信息
public static Binding GetBinding( BindableObject self, BindableProperty property) { var methodInfo = ...
- 梦想CAD控件com接口扩展数据
随着CAD应用软件的飞速发展,经常需要保存一些与图形可视性无关的数据,即非图形参数.例如在绘制化验样图中包含品位数据.MxCAD定义了一类参数——实体扩展数据.扩展数据与实体的可视性无关,而是用户根据 ...