从3月开始研究Openfire,其实就是要做一套IM系统,也正是这个原因才了解到Openfire。之前还真没想过有这么多的开源产品可以做IM,而且也没想到XMPP这个协议竟然如何强大。看来还是标准为先,好的标准可以推动产业发展啊。

Openfire的搭建与简单的demo之前写过篇《技术笔记:XMPP之openfire+spark+smack》,当时主要关注的怎么让这套体系跑起来吧,只不过现在还是在这个阶段,只是多学了点东西留下点笔记吧。

1、对于XMPP的学习很重要

最开始觉得搭建一套Openfire+spark太简单啦,而且将spark的界面修改一下就可以变成一个新的产品,所以当时觉得XMPP协议这么高深的东西不用太深入。只不过随着简单的事情结束了才发现,最核心的还是协议本身,了解协议可以更了解系统的运作,才能体会到这套系统是有多复杂。当然是对于我来说有点复杂,特别是涉及到前后端结合设计与开始时。

为此我推荐一个国内的XMPP协议翻译网站:http://wiki.jabbercn.org/%E9%A6%96%E9%A1%B5。

当然如果英文好那就原版吧:http://xmpp.org/about/technology-overview.html

经过一段时间学习后,感觉QQ和微信在基础原理上真的和XMPP很类似,只是使用的协议格式有些差别,或许这就是即时通讯的抽象层次吧。但是使用XML这种标记语言是不是很浪费流量呢?虽然XMPP扩展起来非常方便,但是就这些标签也着实够大的,像平常的文字聊天时,或许中间标记产生的流量也和聊天内容相当了。毕竟我还没到这种需要考虑大流量的阶段,所以这只是一个想法而已。

2、Openfire的一些设计点与思路

Openfire的源代码整体看了看还是比较清晰的,扩展上支持插件与组件模式。在最近扩展的中发现openfire的源代码本身不太好去修改,依赖性很强,唯独模块间的依赖比较松散些,模块内的类依赖基本是紧耦合的。只不过Openfire可以通过插件扩展,对源代码本身的依赖就小了许多,所以说整体来说还是很不错的。

在Openfire中的插件扩展方式主要是:

  • IQHandler

在XMPP协议中IQ包是指的信息/查询,可以用于服务器与客户端之间进行数据查询,Openfir中实现了一个IQRouter来处理IQ包。自然IQHandler就是具体的IQ包处理单元啦。IQHandler是基于namespace来进行拦截处理的,自定义自己的命名空间后即可。

IQHandler提供了两个抽象方法,用于派生类实现:

    /**
* Handles the received IQ packet.
*
* @param packet the IQ packet to handle.
* @return the response to send back.
* @throws UnauthorizedException if the user that sent the packet is not
* authorized to request the given operation.
*/
public abstract IQ handleIQ(IQ packet) throws UnauthorizedException; /**
* Returns the handler information to help generically handle IQ packets.
* IQHandlers that aren't local server iq handlers (e.g. chatbots, transports, etc)
* return <tt>null</tt>.
*
* @return The IQHandlerInfo for this handler
*/
public abstract IQHandlerInfo getInfo();

handleIQ方法就是解包和业务处理过程,最后返回结果包。

getInfo是用于返回当前IQHandler的命令空间

然后要使这个handler生效还需要注册到IQRouter,可以在插件启动时创建IQHandler的对象并add进去:

@Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
iqHandler = new IQGroupChatHander();
iqRouter = XMPPServer.getInstance().getIQRouter();
iqRouter.addHandler(iqHandler);
} @Override
public void destroyPlugin() {
iqRouter.removeHandler(iqHandler);
}
  • Compoent

Compoent也是一种比较常用的扩展方法,可以通过对特定子域的包进行处理。比如MUC通过注册不同的Service,每个Service都有一个subdomain,系统会将不同的subdomain的数据包分发到专门服务中处理。这样就带来了一个好处,可以完全自定义一套子域的包处理业务,后面实现公众号订阅号就想通过这种思路来解决。而且Openfire还有远程组件的机制,可以扩展成为一个独立的业务系统,这样openfire可以只充当消息处理的核心。

具体的应用也比较简单,实现Component接口,并注册到ComponentManager中。而Component接口中最为重要的方法就是processPacket方法,代码如下:

    /**
* Processes a packet sent to this Component.
*
* @param packet the packet.
* @see ComponentManager#sendPacket(Component, Packet)
*/
public void processPacket(Packet packet);

注意方法中的参数是Packet,这个表示所有子域下的通讯原语都可以用于这里处理。

注册与退出的方法如下:

       componentManager = ComponentManagerFactory.getComponentManager();
try {
componentManager.addComponent("subdomain", this);
}
catch (Exception e) {
Log.error(e.getMessage(), e);
System.err.println(e);
} try {
componentManager.removeComponent("subdomain");
} catch (ComponentException e) {
Log.error(e.getMessage(), e);
}
  • PacketInterceptor

在Openfire中所以的传输都是基于packet,在packet上再派生出不同的通讯原语,如message、roster、JID、IQ等等。基于这个原理只要提供一套对于包的拦截机制就可以实现一套比较强大的扩展机制。在Openfire中就提供了这样的机制处理。在IQRouter\PresenceRouter\MessageRouter中都提供了对于包的拦截器。

// Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, false);

拦截器都注册在InterceptorManager中,在路由处理包时都会调用拦截器,上面的代码就是在路由中截取的代码例子。

那么同样的实现一个拦截器PacketInterceptor接口,并注册到InterceptorManager即可。

InterceptorManager.getInstance().addInterceptor(interceptor);

InterceptorManager.getInstance().removeInterceptor(interceptor);

有了以上三种方法,Openfire可以发展出各种用法,所以官方自己也实现了放多插件供使用。在此也建议对于openfire的扩展最好还是使用插件吧,除非自己的定制要求很高,Openfire本身已经不适应了的。

我的要求基本都可以达成,而且这样以后升级新版本也非常简单,不会出现问题。

3、Spark的纠结

Spark同样出自于jivesoftware,但感觉扩展上就不那么好了。也许是我没有完全弄明白它的扩展原理吧。其实我的需求是重写Spark的UI,同时加入自己的功能,比如群、订阅号等。最开始想着Spark也是支持插件的,但是最后改代码时才发现,里面依赖太深了,基本上和界面相关的都存在依赖,最后可能都要重写一套。

其实在Spark中是有一个UIComponentRegistry类的,一些主要的界面都在这个类中注册的。但可恶的是这些注册的类大多都不能派生出新类来替换这些注册的类。比如

private static Class<? extends ChatRoom> chatRoomClass = ChatRoomImpl.class;

这是聊天窗口的注册类,那么如果我想写一个自己的聊天窗口,是不是直接把这个注册类替换即可呢?不行,因为在其他代码会尽然会这样使用

    @Override
public void filesDropped(Collection<File> files, Component component) {
if (component instanceof ChatRoomImpl) {
ChatRoomImpl roomImpl = (ChatRoomImpl) component; for (File file : files) {
SparkManager.getTransferManager().sendFile(file,
roomImpl.getParticipantJID());
} SparkManager.getChatManager().getChatContainer()
.activateChatRoom(roomImpl);
}
}

在另一个类里尽然直接使用派生类进行了类型判断,这还只是一个点,类似的点太多了。所以最开始想着通过派生UIComponentRegistry中注册的类来达到预期目的已经不大可能了。再加上时间有限,也就懒得管这么多了,就让开发直接在源代码上改。

可恶的是2.7.7版本升级时发现代码大变,这个版本升级smack4.x版本,而且大量使用了1.8的新特性。所以又经过了一番代码合并才升级上来。另外说到smack基本不提供扩展,只提供事件的订阅。

只不过spark是跨平台的,很容易就能在mac下运行,而且代码是java的,暂时还不想抛弃掉,等将来考虑是不是再重写吧。

Openfire阶段实践总结的更多相关文章

  1. openfire的组件(Component)开发

    在之前的文章<Openfire阶段实践总结>中提到过一种openfire的扩展模式Compoent.本文将主要探讨对这种模式的应用与开发方法. 内部与外部组件介绍 在openfire中的许 ...

  2. 分享下对JAVA程序员成长之路的总结<转>

    我也搞了几年JAVA了,由于一向懒惰,没有成为大牛,只是一普通程序猿,手痒来给新人分享下从新手成长为老鸟的已见.   首先初识语法的阶段,必须要学会怎么操作对象,操作if和for,操作list set ...

  3. 【转载】分享下多年积累的对JAVA程序员成长之路的总结

    注:该文是从百度贴吧转载过来,之前看到觉得写得还不错,对Java开发学习者来说很有意义的,可以看看. 我也搞了几年JAVA了,由于一向懒惰,没有成为大牛,只是一普通程序猿,不爱玩社交网站,不爱玩微博, ...

  4. java学习大方向

    总结Java程序员成长之路   转载  http://bbs.javazhijia.com/topic/1bb0733f80d94aedb50cc3b66d9792b6.html 我也搞了几年JAVA ...

  5. Java学习之路(转)

    我也搞了几年JAVA了.因为一向懒惰,没有成为大牛,仅仅是一普通程序员,不爱玩社交站点.不爱玩微博,只有喜欢百度贴吧,潜水非常久了,手痒来给新人分享下从新手成长为老鸟的已见,也刷刷存在感,应该不比曝照 ...

  6. 分享下多年积累的对JAVA程序员成长之路的总结

    http://blog.csdn.net/zhongzelin/article/details/8643269我也搞了几年JAVA了,由于一向懒惰,没有成为大牛,只是一普通程序猿,不爱玩社交网站,不爱 ...

  7. java实习生的成长之路<转>

    首先初识语法的阶段,必须要学会怎么操作对象,操作if和for,操作list set map,然后是线程.IO和jdbc什么的,其余的,若是一时不理解,可以后边需要时再学. 这阶段完了,你可以写些能在控 ...

  8. 阿里云场景化阿里云企业数字化转型售前方法PSA

    阿里云场景化阿里云企业数字化转型售前方法PSA 目录 01 课程收获 理解企业数字化转型的概念.内涵.本质 了解企业数字化转型的要点.目标和切入点 掌握数字化转型项目售前阶段实践方法 场景化方案 阿里 ...

  9. Dockerfile 多阶段构建实践

    写在前面 在Docker Engine 17.05 中引入了多阶段构建,以此降低构建复杂度,同时使缩小镜像尺寸更为简单.这篇小作文我们来学习一下如何编写实现多阶段构建的Dockerfile 关于doc ...

随机推荐

  1. Javascript 的执行环境(execution context)和作用域(scope)及垃圾回收

    执行环境有全局执行环境和函数执行环境之分,每次进入一个新执行环境,都会创建一个搜索变量和函数的作用域链.函数的局部环境不仅有权访问函数作用于中的变量,而且可以访问其外部环境,直到全局环境.全局执行环境 ...

  2. python核心编程第二版练习题答案

    2-5 #写一个while循环,输出整型为0~10 a=0while a<11: print a a+=1 #写一个for循环重复以上操作 for i in range(11): print i ...

  3. VisualVM通过jstatd方式远程监控远程主机

    配置好权限文件 [root@test bin]# cd $JAVA_HOME/bin [root@test bin]# vim jstatd.all.policy grant codebase &qu ...

  4. 分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)

    分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...

  5. 【趣事】用 JavaScript 对抗 DDOS 攻击 (下)

    上一篇:http://www.cnblogs.com/index-html/p/js-network-firewall.html 对抗 v2 之前的那些奇技淫巧,纯属娱乐而已,并不能撑多久. 但简单. ...

  6. .NetCore中的日志(2)集成第三方日志工具

    .NetCore中的日志(2)集成第三方日志工具 0x00 在.NetCore的Logging组件中集成NLog 上一篇讨论了.NetCore中日志框架的结构,这一篇讨论一下.NetCore的Logg ...

  7. jQuery学习之路(6)- 简单的表格应用

    ▓▓▓▓▓▓ 大致介绍 在CSS技术之前,网页的布局基本都是依靠表格制作,当有了CSS之后,表格就被很多设计师所抛弃,但是表格也有他的用武之地,比如数据列表,下面以表格中常见的几个应用来加深对jQue ...

  8. [原]Cachedb 网络模块文档

    Cachedb 网络模块文档 整体结构 多路复用 (epoll 模块) 事件驱动 (事件封装) 缓冲管理 (上层buffer管理) 设计思想 层次化的设计,每一个模块只调用上一个模块的接口,并将耦合聚 ...

  9. Django admin定制化,User字段扩展[原创]

    前言 参考上篇博文,我们利用了OneToOneField的方式使用了django自带的user,http://www.cnblogs.com/caseast/p/5909248.html , 但这么用 ...

  10. Maven(一)linux下安装

    1.检查是否安装JDK,并且设置了环境变量(JAVA_HOME): echo $JAVA_HOME java -version 运行结果: 显示jdk的安装路径,和java的版本,如: #jdk路径 ...