自 3 月起開始研究 Openfire,目標是構建一套完整的即時通訊(IM)系統。這才發現開源界有這麼多成熟的 IM 解決方案,而 XMPP 協議的強大更是超出預期。標準協議確實能推動產業發展——這是這段經歷給我最深的體會。
Openfire 的搭建與基礎 Demo 此前已在《技術筆記:XMPP之 openfire+spark+smack》中記錄。當時重點在於跑通流程,如今雖仍處於同一階段,但積累了更多實戰經驗,特此總結。
初期以為搭建 Openfire + Spark 很簡單,修改 Spark 界面就能產出新產品,因此覺得 XMPP 協議無需深究。然而隨著基礎功能完成,發現協議本身才是核心。唯有理解協議,才能洞悉系統運作機制,體會其複雜性——特別是前後端結合設計時。
推薦學習資源:
學習後發現,QQ 和微信在基礎原理上與 XMPP 高度相似,僅協議格式有差異。XML 標記語言雖擴展性強,但標籤開銷較大,普通文字聊天時標記流量可能與內容持平。不過目前業務量未達到需優化流量的階段,僅作記錄。
Openfire 源碼結構清晰,支援插件與組件兩種擴展模式。源碼模組間依賴較鬆散,但模組內類耦合度高。幸好插件機制降低了對核心源碼的依賴,整體擴展性不錯。
XMPP 中 IQ 封包用於客戶端與服務端數據查詢。Openfire 實現 IQRouter 路由 IQ 封包,IQHandler 基於命名空間攔截處理。需實現兩個抽象方法:
public abstract IQ handleIQ(IQ packet) throws UnauthorizedException; public abstract IQHandlerInfo getInfo();
handleIQ:解包、業務處理、返回結果封包getInfo:返回當前 Handler 的命名空間資訊插件啟動時註冊至 IQRouter:
@Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
iqHandler = new IQGroupChatHandler();
iqRouter = XMPPServer.getInstance().getIQRouter();
iqRouter.addHandler(iqHandler);
}
@Override
public void destroyPlugin() {
iqRouter.removeHandler(iqHandler);
}
通過處理特定子域封包實現擴展。MUC 即通過註冊不同 Service(各擁有 subdomain),系統將不同子域封包分發至專門服務處理。這允許完全自定義子域業務邏輯,適合實現公眾號、訂閱號等功能。Openfire 還支援遠程組件,可將業務系統獨立部署,Openfire 僅作消息路由核心。
實現 Component 接口並註冊至 ComponentManager,核心方法 processPacket(Packet packet) 處理所有子域通訊原語。
componentManager = ComponentManagerFactory.getComponentManager();
componentManager.addComponent("subdomain", this);
// 退出時
componentManager.removeComponent("subdomain");
Openfire 所有傳輸基於 Packet,衍生出 Message、Roster、JID、IQ 等原語。InterceptorManager 提供統一攔截機制,在 IQRouter、PresenceRouter、MessageRouter 中均會調用攔截器:
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, false);
實現 PacketInterceptor 接口並註冊即可:
InterceptorManager.getInstance().addInterceptor(interceptor); InterceptorManager.getInstance().removeInterceptor(interceptor);
建議:除非極高定製需求,優先使用插件擴展,升級新版本時無需合併源碼,維護成本極低。
同屬 JiveSoftware,但 Spark 擴展性遠不如 Openfire。需求是重寫 UI 並加入群組、訂閱號等功能。雖 Spark 支援插件,但內部依賴極深,界面相關類幾乎都有硬編碼依賴,繼承替換幾乎不可行。
例如 UIComponentRegistry 註冊聊天窗口類 ChatRoomImpl,想替換為自定義實現,卻發現其他類直接用 instanceof ChatRoomImpl 判斷類型,此類硬編碼點太多。最終決定直接修改源碼。
2.7.7 版本升級到 Smack 4.x 並大量使用 Java 1.8 特性,代碼大幅變動,合併極其痛苦。Smack 基本不提供擴展,僅支援事件訂閲。Spark 跨平台、Java 編寫,暫時保留,未來再評估是否重寫。
Openfire 作為成熟 XMPP 服務端,擴展機制完善,適合作為 IM 系統核心。深入理解 XMPP 協議是驾驭系統的前提。客戶端選型需謹慎評估擴展性,避免陷入源碼修改泥潭。