初看Tigase的packet內部流轉機制一開始不是太明白。裏面用到了較多的線程,代碼不太看得懂。慢慢的通過一條消息的請求和響應的代碼跟蹤分析,搞清楚了消息流轉的過程。
前言
本文使用Tigase Server version:7.0.2 進行的代碼跟蹤和分析。
使用工具:IntelliJ IDEA14.1.4
Tigase通過tigase.io包當中的代碼讀取網絡中的字節數組,然後通過tigase.net包當中的類把字節數組轉換爲字符,最後通過tigase.xml包當中的XML解析器把這些字符轉換成XML DOM對象。
圖片流程說明
看tigase源碼你會發現所有的tigase處理都是基於多線程,每個component都有自己的in和out處理線程,線程間的數據傳輸通過queue
總的流程大致就是:
文字流程說明
下面是請求和響應的步驟說明(只列出了關鍵的步驟)。
A. 從client到server的過程(請求-Request)
ClientConnectionManager和MessageRouter都間接或直接繼承了AbstractMessageReceiver。
a. XMPPIOService負責接收客戶端報文並轉換爲對應的packet放入隊列receivedPackets(相當於in_queues)中,而SocketThread負責創建ReadThread和WriteThread線程,
ResultsListener則是SocketThread的一個內部類。ResultsListener負責調度socketReadThread()和socketWriteThread(), Client2Server讀取和寫入數據包的IO操作主要由XMPPIOService來完成,XMPPIOService實例則在read和write線程中被使用。
b. ClientConnectionManager則負責從XMPPIOService獲取receivedPackets,並對收到的報文進行處理,並將處理後的報文放入MessageRouter的out_queues隊列中。
c. MessageRouter重寫了AbstractMessageReceiver的processPacket方法,在該方法中MessageRouter通過packet.getTo()得到組件的名稱並轉發packet到該組件的in_queues隊列中,該目標服務組件(如ses-man,即SessionManager)可能在本機服務器上,也可能在該域(domain)集羣的其他服務器上運行着,MessageRouter的查找順序是先查本機服務器,找不到的話再去集羣中查找;如果找到則將packet轉發給目標組件處理,如果沒找到則返回沒有找到目標組件的錯誤消息。
d. 我們的組件一般都有自己的in_queues和out_queues. 當前組件的in_queues的數據來自於上一組件的out_queues。
int queueIdx = Math.abs(hashCodeForPacket(packet) % in_queues_size);
boolean result = in_queues.get(queueIdx).offer(packet, packet.getPriority().ordinal());//#這裏的packet數據來自於packetFrom="xxx",即傳給當前組件的上一組件。
B.從Service到Client的過程(響應-Response)
服務端響應的數據也是放到out_queues中的,各組件的對應的線程會對out_queues中的packet的to屬性做解析,並將消息轉發到指定目標。<ping xmlns='urn:xmpp:ping'/>
</iq>
public abstract class IOService<RefObject> /**接收報文後call->處理客戶端報文數據信息*/ @Override
/** ... ... }} 返回消息前還調用了下面兩個類的對應write方法
SocketIO.java @Override }TLSIO.java public class TLSIO implements IOInterface { private int writeBuff(ByteBuffer buff) throws IOException { }} |
IOService打包好了消息之後形成響應的packet包,->然後再執行到ConnectionManager的writePacketToSocket()方法->再執行到ClientConnectionManager的processPacket方法。
ConnectionManager
ClientConnectionManager
至此本次消息的請求和響應結束!server會繼續通過AbstractMessageReceiver獲取下一條消息進行處理,如此循環。。。
下面的資料希望對你也有所幫助
網絡
connectionManager同時協調ConnectionOpenThread與SocketThread。
ConnectionOpenThread脫離上述組件,屬於網絡層實現,操作selector。它負責Selector.open。
IOService提供線程安全的call方法,XMPPIOService繼承它,保存了連接信息,每個連接一個IOService。
SocketThread在實例化時,會啓動多個線程,同時盯住selector。負責將每個確定的IOService進行數據處理。
實現ConnectionOpenListener接口accept方法接收SocketChannel,組裝IOService,交由SocketThread處理。
ConnectionManager用ConcurrentHashMap記錄了所有的連接。
零碎
AbstractMessageReceiver.addPacket 往自己的in_queue里加數據,是阻塞的,如果滿了會出事。
AbstractMessageReceiver.addPacketNB 往自己的in_queue里加數據,非阻塞的,和上一個的區別在於,一個是put一個是offer到queue。
AbstractMessageReceiver.addPackets 來一堆數據。
所有in_queue裏的數據,會被processPacket方法所處理。
對應有addOutPacket。
所有out_queue裏的數據,都默認扔給parent的in_queue,沒有parent就扔到自己的in_queue。
所有in_queue的數據,都由processPacket具體的實現來處理。
tigase.server.ServerComponent – 這是一個非常基本的component接口。所有的component都必須實現接口中定義的方法。
tigase.server.MessageReceiver – 這個接口extends ServerComponent,所有希望接收數據packets的Component都需要實現接口中定義的方法,比如session manager和c2s connection manager。
tigase.conf.Configurable – 如果希望components可以被配置,則需要實現這個接口,所有默認定義基本都在這。在運行時,配置信息會被推送到這種類型的對象。components必須能夠在運行時對變更的配置項進行處理,這一點在實現時要留神。
tigase.disco.XMPPService – 實現了這個對象的類可以對“ServiceDiscovery”請求做出響應。
tigase.stats.StatisticsContainer – 實現了這個對象的類可以返回運行時的統計信息。任何一個對象都可以實現這個接口用來收集統計信息
tigase.server.AbstractMessageReceiver –
它已經實現了四個接口:ServerComponent,MessageReceiver,Configurable和StatisticsContainer。它通過自己的多個線程來管理內部數據隊列,且能避免死鎖。