openfire,做为一个实现xmpp的即时通信服务器端,自身提供了集群的实现,目前,理论上,能支持N个节点的集群,即节点无限制
看了下他集群的实现,他是用到了oracle 的coherence的中间件,关于coherence的话,没有过多研究,大概知道,他是一个缓存中间件,支持缓存集群,而且支持缓存改变事件触发!知道这些就够了,这些就能用来理解openfire集群的原理了
coherence集群开启以后,会为每一个节点生成一个唯一的nodeID,openfire就是使用这个nodeId,来区分当前缓存中对应的Presence,Message等是属于哪一个节点!openfire集群大概工作过程如下:
1,切换缓存实现,这时,缓存层会有其默认的本地缓存,切换到coherence缓存!通过其定义的策略接口,完成这一切换!当然此时,openfire实现的时候,又定义了一个变量用来继续保持本地缓存的实现实例,以便于在openfire的控制台上能再次将缓存层切换为本地缓存。见类 ClusteringPlugin中的代码实现
2,开启coherence集群,这块需要简单了解下coherence集群的使用,具体的可以百度一下!代码见类CoherenceClusteredCacheFactory中的startCluster方法!其中有一段代码,是判断当前最大支持多少个节点!这块以前可能jive打算,集群插件是收费的,所有加了这段代码,不过开源之后,这块就没啥用了,可以直接删除了,都没问题
3,添加member节点事件监听,添加缓存内容监听!代码见类CoherenceClusteredCacheFactory中的startCluster方法。member监听的话,可以监听到当前coherence集群中,有多少个节点,是否有新节点加入或有节点退出等。
下面重点来了,他如果实现,节点a中的用户向节点b中的用户发信息的呢。
首先,我们追踪类 RoutingTableImpl 中的 RoutingTableImpl 方法,看其中一段代码:
// This is a route to a local user hosted in other node
if (remotePacketRouter != null) {
routed = remotePacketRouter
.routePacket(clientRoute.getNodeID().toByteArray(), jid, packet);
}
就是这段代码,他调用了remotePacketRouter类中的route方法,继续追这个方法的实现类 CoherencePacketRouter 中的方法,有如下实现
CacheFactory.doClusterTask(new RemotePacketExecution(receipient, packet), nodeID);
对,重点就在标红的部分,doClusterTask 我们看他的实现
CoherenceClusteredCacheFactory类中的doClusterTask(final ClusterTask task, byte[] nodeID) 方法
Set setMembers = taskService.getInfo().getServiceMembers();
// Remove all members except requested nodeID
for (Iterator it=setMembers.iterator(); it.hasNext();) {
Member member = (Member) it.next();
if (!Arrays.equals(member.getUid().toByteArray(), nodeID)) {
it.remove();
}
}
// Check that the requested member was found
if (!setMembers.isEmpty()) {
// Asynchronously execute the task.
taskService.execute(buildInvocable(task), setMembers, null);
return true;
}
这里,他获得的coherence集群中的所有节点集合,并将非指定nodeID的所有节点移除,并在指定的节点执行了一个任务,task,即ClusterTask接口的实现类
最下面,我标红的部分,就是在指定node上执行指定任务的代码,但里面传的参数是一个Invocable实现,这个是coherence定义的接口。看 buildInvocable 做了哪些工作,看代码:
private static Invocable buildInvocable(final ClusterTask task) {
return new AbstractInvocable() {
public void run() {
task.run();
}
public Object getResult() {
return task.getResult();
}
};
}
很简单,这个实现,把openfire定义的ClusterTask包装成了一个Invocable ,这样就可以使用coherence提供的方法,在指定节点上,执行ClusterTask中run的部分
说过了原理部分,我们找一个ClusterTask的实现,看看如何实现的在集群上发送信息
BroadcastMessage 类中的run方法,如下:
public void run() {
// Broadcast message to client sessions connected to this node
XMPPServer.getInstance().getRoutingTable().broadcastPacket(packet, true);
}
对,就一行代码,调用本地的RoutingTable,去直接发送信息!重点是,这个代码,不是在本地执行的,而是在指定节点上执行的!具体怎么做到的,这个要问问coherence了
了解了这些的话,就大概知道了openfire集群的实现原理,自己也可以根据这些,实现一套自己的集群!
有对这块研究多的,可以相互交流,邮箱 [email protected]