天天看點

基于Netty的IM簡單實作原理

最近在開發MobIM,實作了消息傳輸和群等功能的IM功能。SDK功能包小,而功能全面。可以與原來的系統進行無縫整合。

自己抽空也實作了一套IM Server和IMClient的業務通信模式。沒有實作複雜的UI界面,實作簡單的登入注冊,發消息,收消息。伺服器端與用戶端都使用Netty通信。

Netty基于非阻塞(nio),事件驅動的網絡應用程式架構和工具。

通過Netty面對大規模的并發請求可以處理的得心用手。用來替代原來的bio網絡應用請求架構。

BIO通信即平時使用的基于Socket,ServerSocket的InputStream和OutStream。

Netty神奇的地方在于是否是阻塞的。

在BIO模型中,伺服器通過ServerSocket來開啟監聽,每當有請求的時候開啟一個線程來接受處理和維持狀态。這種思想在低并發,小吞吐的應用還可以應付,一旦遇到大并發,大吞吐的請求,必然歇菜。線程和用戶端保持着1:1的對應關系,維持着線程。維持那麼的多的線程,JVM必然不堪重負,伺服器必然崩潰,當機。

而在非阻塞的Netty中,卻可以應付自如。從容應對。Tomcat就是基于BIO的網絡通信模式(Tomcat可以通過一定配置,改成非阻塞模式),而JBoss卻是基于非阻塞的NIO實作。

用Netty來實作IM實在太合适了。可以在最短的時間裡整出一套思路清晰,架構簡明的IM通信底層模型。提下需求,底層用JSON 字元串String進行通信,對象通過JSON序列化成JSON String。收到JSON資料後再反序列化成對象。

首先,我們先看伺服器是怎麼實作的。

以上是Netty伺服器啟動的代碼。其中需要注意childHandler方法。需要把我們要添加的業務處理handler來添加到這裡。通過ChannelPipeline 添加ChannelHandler。而處理字元串的就在IMServerHandle裡實作。IMServerHandle繼承了SimpleChannelInboundHandler類。其中泛型T就是要轉換成的對象。用戶端與伺服器端通信是本質上通過位元組碼byte[]通信的,而通過StringDecoder 和StringEncoder工具類對byte[]進行轉換,在IMServerHandle中擷取到String進行處理即可。

看下IMServerHandle的實作方式。

通過IMServerHandle可以十分友善的處理擷取到的String字元串。處理完後,可以直接通過ChannelHandlerContext的writeAndFlush方法發送資料。

再看下Netty用戶端如何實作。

Netty的client端實作和Server實作方式大同小異。比Server端要簡要些了。少一個NIOEventLoop。在Bootstrap 的handle方法中增加ChannelInitializer初始化監聽器,并增加了IMClientHandler的監聽操作。其中IMClientHandler具體處理伺服器傳回的通信資訊。

通過ChannelFuture 擷取Channel,通過Channel在一個循環裡發送請求。如果消息隊列BlockingQueue非空的時候,擷取Request并發送。以上發送,如何接受資料呢?接受到的json被反序列化直接變成了對象Response,對Response進行處理即可。

定義了一個消息接受到的監聽接口。

在接口onMessageReceived方法裡直接對擷取成功的響應進行處理。

而伺服器端對某個用戶端進行發送操作,把Channel添加到ChannelGroup裡,将uname和channelid對應起來。需要對某個使用者發送消息的時候通過uname擷取channelid,通過channelid從ChannelGroup裡擷取channel,通過channel發送即可。

具體操作如下:

通過以上代碼解析應該對IM的通信模式有了比較全面的認識。具體實作過程可以下載下傳源代碼進行檢視。歡迎大家回報提出問題。

<a href="https://github.com/sinxiao/NettyIMServerAndAndroidClient">https://github.com/sinxiao/NettyIMServerAndAndroidClient</a>

基于Netty的IM簡單實作原理

運作效果圖。