从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. 【C#附源码】数据库文档生成工具支持(Excel+Html)

    [2015] 很多时候,我们在生成数据库文档时,使用某些工具,可效果总不理想,不是内容不详细,就是表现效果一般般.很多还是word.html的.看着真是别扭.本人习惯用Excel,所以闲暇时,就简单的 ...

  2. git克隆项目到本地&&全局安装依赖项目&&安装依赖包&&启动服务

     一.安装本地开发环境 1.安装本项目 在需要保存到本地的项目的文件夹,进入到文件夹里点击右键,bash here,出现下图: 2.安装依赖项目  3.安装依赖包(进入到命令行) # 安装依赖包 $ ...

  3. 【知识必备】ezSQL,最好用的数据库操作类,让php操作sql更简单~

    最近用php做了点小东东,用上了ezSQL,感觉真的很ez,所以拿来跟大家分享一下~ ezSQL是一个非常好用的PHP数据库操作类.著名的开源博客WordPress的数据库操作就使用了ezSQL的My ...

  4. [转载]Cookie/Session的机制与安全

    Cookie和Session是为了在无状态的HTTP协议之上维护会话状态,使得服务器可以知道当前是和哪个客户在打交道.本文来详细讨论Cookie和Session的实现机制,以及其中涉及的安全问题. 因 ...

  5. (翻译)FIFO In Hardware

    翻译一些自己觉得有价值的材料,工作中碰到英语大多数是读,基本没有写或者翻的,翻得不好不到位的敬请指摘. 同时也附原文以供参考. http://electronics.stackexchange.com ...

  6. 移动应用App测试与质量管理一

    测试工程师 基于Html的WebApp测试, 现在一些移动App混Html5 HTML5性能测试 兼容性 整理后的脑图 测试招聘 弱化大量技术考察 看重看问题的高度 看重潜力 测试经验 质量管理 专项 ...

  7. 整理下.net分布式系统架构的思路

    最近看到有部分招聘信息,要求应聘者说一下分布式系统架构的思路.今天早晨正好有些时间,我也把我们实际在.net方面网站架构的演化路线整理一下,只是我自己的一些想法,欢迎大家批评指正. 首先说明的是.ne ...

  8. 张高兴的 UWP 开发笔记:汉堡菜单进阶

    不同于Windows 8应用,Windows 10引入了"汉堡菜单"这一导航模式.说具体点,就拿官方的天气应用来说,左上角三条横杠的图标外加一个SplitView控件组成的这一导航 ...

  9. 如何使用dos命令打开当前用户、当前日期、当前时间以及当前用户加当前时间?

    1.dos命令安装mysqld --stall.启动net start mysql.进入MySQL数据库mysql -uroot -p后,输入select user();当前用户 select cur ...

  10. 基于select的python聊天室程序

    python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...