jinterface包详解
jinterface阅读总结
一.jinterface简介
jinterface是用来与erlang node通信的一个java包。由于大多东西都已经封装好了,所以使用起来非常方便。它伪装自己是一个节点,在erlang程序看来,它就是一个erlang node
二.jinterface组成
jinterface中有很多类,我觉得可以分为两种,一种是基本数据类型的分装,一种是与erlang通信的类。
基本数据类型主要以OtpErlang开头。使用起来十分简单,例如创建两个{hello, world}的元组如下:
OtpErlangAtom hello = new OtpErlangAtom("hello");
OtpErlangAtom world = new OtpErlangAtom("world");
OtpErlangTuple tuple = new OtpErlangTuple(new OtpErlangObject[]{hello, world});
所有的erlang基本类型都是从OtpErlangObject继承,所以一个其他类型数据类型都可以很方便转成OtpErlangObject.在这些类中封装了简单的对这些类型的操作。比如得到其中的值,序列化等等。
-
AbstractNode类
:虽然这个类叫做AbstractNode,但是它并不是一个abstract类,不过它的构造函数是protectd,所以不能直接构造此类,只能继承。构造函数接受0个参数或者接受一个主机名跟一个cookie作为参数。在AbstarctNode类加载的时候,会初始化localhost跟默认cookie。类中还有一些其他字段,进行了一些简单封装。 -
OtpLocalNode类
:此类继承AbstractNode类,所以也需要跟父类一样的参数作为构造函数,此类提供主要提供了创建pid,创建port,创建ref等函数。这些函数主要依照一定的格式创建对应的数据类型。比如pid:分为两个部分,一个部分是pidCount,一个部分是serial,pidCount不断增加当pidCount超过32767时把serial+1,pidCount归零。serial超过8191时归零。port的规则是:超过268435455时归零。ref的规则是:ref内部用int[3]表示,当int[0]超过262143时,int[1]+1并把int[0]归零,而int[2]只在int[1]==0时自增。由于溢出后会变成最大负数,所以相当于把int 当做unsigned int来使用。 -
OtpNode类
:此类继承自OtpLocalNode,所以自带主机名跟cookie和创建pid,port,ref的能力。并且在构造函数中多了一个port的参数。在这个类中有两个内嵌类,有着重要的作用,它们是Acceptor和Mailboxes,先说说Acceptor类,此类继承自Thread,也就是说此类可以可以调用start方法创建一个线程,线程运行此类的run方法。在OtpNode初始化的时候会初始化Acceptor类,此类会初始化必要字段,创建与epmd通信的监听端口,并把端口通过OtpEpmd发送给epmd。然后调用start创建线程运行run方法,在run方法中不断接受监听的sock到来的链接,并放到OtpNode的hashtable中。其他方法都是初始化或者关闭的时候释放资源。Mailboxes类:此类中有两个hashtable如下(WeakReference表示弱引用,不加引用计数):
private Hashtable<OtpErlangPid, WeakReference> byPid = null;(pid到otpmobx的映射)
private Hashtable<String, WeakReference> byName = null;(string到otpmbox的映射)
Mailboxes类提供创建OtpMbox的函数,实现方法是构造出OtpMbox并存放到两个hashtable中。 剩下的函数就是通过名字得到OtpMbox,根据pid得到OtpMbox等待。大概来说,Acceptor主要是向epmd注册,并接受到来的连接。Mailboxes主要是创建OtpMbox并提供索引。现在我们回头看看OtpNode类的其他方法:创建OtpMbox什么的主要调用Mailboxes类,有个非常有用的ping方法,主要实现是通过OtpMbox的send函数发送一定格式的数据再通过返回的数据判断是否连通,稍后我们再来看OtpMbox类。其他跟消息发送有关的函数都通过创建一个OtpMbox来实现。还有就是提供对连接节点的管理。比如移除连接,添加连接等。 -
GenericQueue类
:此类用链表的方式实现了一个通用的队列,提供获取,插入等操作,提供同步的get的操作等 -
OtpEpmd类
:主要负责跟epmd进行通信,提供查找端口等方法。 -
OtpMbox类
:OtpMbox类,提供获取消息的方法,而消息的来源是从OtpNode中的OtpCookedConnection字段新开一个线程不断读取消息,然后路由到各个OtpMbox. -
OtpCookedConnection类
:继承至AbstractConnection类,主要实现AbstractConnection中的dliver方法,把消息路由到何处,而OtpCookedConnection则把消息路由到OtpNode得OtpMbox下。 -
OtpConnection类
:此类也继承AbstractConnection类,它有着自己的消息队列,所以把消息存放在自己的队列中。 -
OtpMsg类
:封装消息必要的信息,比如消息从哪儿来,到哪儿去,消息的内容等等。 -
OtpOutputStream类
:继承至ByteArrayOutputStream,提供对erlang消息的读取,我们知道,erlang节点之间进行消息通信,使用的是external格式,在erlang中可以使用term_to_binary进行转换,那么这个类就负责解析这个格式,得到相应的数据。 -
OtpPeer类
:非常简单,继承至AbstractNode,抽象出一个远程节点,其中主要存储了远程节点的节点名字,提供连接此节点的函数,返回一个OtpConnection类。 -
OtpSelf类
:继承至OtpLocalNode类,初始化创建一个监听端口,提供对epmd进行交互。
jinterface使用了多线程来读取网络数据,并没有使用epoll等技术,所以还能进一步优化。
三.jinterface使用例子
下面来看一个小例子,然后结合上面的介绍,看看是如何与erlang结点进行通信的。
public class Main {
public static void main(String[] args) {
// write your code here
try {
Runtime.getRuntime().exec("/usr/local/bin/epmd -daemon");//先启动epmd程序
OtpNode node = new OtpNode("client@127.0.0.1", "erlang");//就像我们前面介绍的,这一步创建一个节点,在初始化函数中会创建一个acceptor,它向epmd注册自己监听的端口,并不断的接受连接的节点,并把接受的连接存在一个hashtable中,连接用OtpCookedConnection类来表示,它会不断的读取到来的消息,并放到对应的mbox中。
OtpMbox mbox = node.createMbox("client");//创建一个mbox,这样OtpCookedConnection就能把相应的消息路由到其中的队列。
while (true){
OtpErlangObject msg = mbox.receive();//不断的从mbox的队列中取出消息。
System.out.println(msg.toString());//打印出来。。。
}
}catch (Exception e){
e.printStackTrace();
}
}
}
四.存在的问题
- 由于OtpErlangString 不是继承自OtpErlangList,所以发过来的空字符串会变成列表对象。而且Unicode字符串发过来是列表的codepoint,所以字符串最好还是使用二进制。