從3月開始研究Openfire,其實就是要做一套IM系統,也正是這個原因才了解到Openfire。之前還真沒想過有這麼多的開源産品可以做IM,而且也沒想到XMPP這個協定竟然如何強大。看來還是标準為先,好的标準可以推動産業發展啊。
最開始覺得搭建一套Openfire+spark太簡單啦,而且将spark的界面修改一下就可以變成一個新的産品,是以當時覺得XMPP協定這麼高深的東西不用太深入。隻不過随着簡單的事情結束了才發現,最核心的還是協定本身,了解協定可以更了解系統的運作,才能體會到這套系統是有多複雜。當然是對于我來說有點複雜,特别是涉及到前後端結合設計與開始時。
經過一段時間學習後,感覺QQ和微信在基礎原理上真的和XMPP很類似,隻是使用的協定格式有些差别,或許這就是即時通訊的抽象層次吧。但是使用XML這種标記語言是不是很浪費流量呢?雖然XMPP擴充起來非常友善,但是就這些标簽也着實夠大的,像平常的文字聊天時,或許中間标記産生的流量也和聊天内容相當了。畢竟我還沒到這種需要考慮大流量的階段,是以這隻是一個想法而已。
Openfire的源代碼整體看了看還是比較清晰的,擴充上支援插件與元件模式。在最近擴充的中發現openfire的源代碼本身不太好去修改,依賴性很強,唯獨子產品間的依賴比較松散些,子產品内的類依賴基本是緊耦合的。隻不過Openfire可以通過插件擴充,對源代碼本身的依賴就小了許多,是以說整體來說還是很不錯的。
在Openfire中的插件擴充方式主要是:
IQHandler
在XMPP協定中IQ包是指的資訊/查詢,可以用于伺服器與用戶端之間進行資料查詢,Openfir中實作了一個IQRouter來處理IQ包。自然IQHandler就是具體的IQ包處理單元啦。IQHandler是基于namespace來進行攔截處理的,自定義自己的命名空間後即可。
IQHandler提供了兩個抽象方法,用于派生類實作:


handleIQ方法就是解包和業務處理過程,最後傳回結果包。
getInfo是用于傳回目前IQHandler的指令空間
然後要使這個handler生效還需要注冊到IQRouter,可以在插件啟動時建立IQHandler的對象并add進去:


Compoent
Compoent也是一種比較常用的擴充方法,可以通過對特定子域的包進行處理。比如MUC通過注冊不同的Service,每個Service都有一個subdomain,系統會将不同的subdomain的資料包分發到專門服務中處理。這樣就帶來了一個好處,可以完全自定義一套子域的包處理業務,後面實作公衆号訂閱号就想通過這種思路來解決。而且Openfire還有遠端元件的機制,可以擴充成為一個獨立的業務系統,這樣openfire可以隻充當消息處理的核心。
具體的應用也比較簡單,實作Component接口,并注冊到ComponentManager中。而Component接口中最為重要的方法就是processPacket方法,代碼如下:


注意方法中的參數是Packet,這個表示所有子域下的通訊原語都可以用于這裡處理。
注冊與退出的方法如下:


PacketInterceptor
在Openfire中是以的傳輸都是基于packet,在packet上再派生出不同的通訊原語,如message、roster、JID、IQ等等。基于這個原理隻要提供一套對于包的攔截機制就可以實作一套比較強大的擴充機制。在Openfire中就提供了這樣的機制處理。在IQRouter\PresenceRouter\MessageRouter中都提供了對于包的攔截器。
攔截器都注冊在InterceptorManager中,在路由處理包時都會調用攔截器,上面的代碼就是在路由中截取的代碼例子。
那麼同樣的實作一個攔截器PacketInterceptor接口,并注冊到InterceptorManager即可。
有了以上三種方法,Openfire可以發展出各種用法,是以官方自己也實作了放多插件供使用。在此也建議對于openfire的擴充最好還是使用插件吧,除非自己的定制要求很高,Openfire本身已經不适應了的。
我的要求基本都可以達成,而且這樣以後更新新版本也非常簡單,不會出現問題。
Spark同樣出自于jivesoftware,但感覺擴充上就不那麼好了。也許是我沒有完全弄明白它的擴充原理吧。其實我的需求是重寫Spark的UI,同時加入自己的功能,比如群、訂閱号等。最開始想着Spark也是支援插件的,但是最後改代碼時才發現,裡面依賴太深了,基本上和界面相關的都存在依賴,最後可能都要重寫一套。
其實在Spark中是有一個UIComponentRegistry類的,一些主要的界面都在這個類中注冊的。但可惡的是這些注冊的類大多都不能派生出新類來替換這些注冊的類。比如
這是聊天視窗的注冊類,那麼如果我想寫一個自己的聊天視窗,是不是直接把這個注冊類替換即可呢?不行,因為在其他代碼會盡然會這樣使用


在另一個類裡盡然直接使用派生類進行了類型判斷,這還隻是一個點,類似的點太多了。是以最開始想着通過派生UIComponentRegistry中注冊的類來達到預期目的已經不大可能了。再加上時間有限,也就懶得管這麼多了,就讓開發直接在源代碼上改。
可惡的是2.7.7版本更新時發現代碼大變,這個版本更新smack4.x版本,而且大量使用了1.8的新特性。是以又經過了一番代碼合并才更新上來。另外說到smack基本不提供擴充,隻提供事件的訂閱。
隻不過spark是跨平台的,很容易就能在mac下運作,而且代碼是java的,暫時還不想抛棄掉,等将來考慮是不是再重寫吧。