Mina文件 04- 會話
介紹
會話是MINA的核心:每次客戶端連線到伺服器時,都會在伺服器上建立一個新會話,並將保留在記憶體中,直到客戶端斷開連線。如果您在客戶端使用MINA,則每次連線到伺服器時,也會在客戶端上建立會話。
會話是MINA的核心:每次客戶端連線到伺服器時,都會在伺服器上建立一個新會話,並將保留在記憶體中,直到客戶端斷開連線。如果您在客戶端使用MINA,則每次連線到伺服器時,也會在客戶端上建立會話。
這也是您在會話中需要執行的任何操作的訪問點:傳送訊息,關閉會話等...
至關重要的是要理解由於NIO的非同步本質,從會話中讀取並沒有多大意義。實際上,當一些傳入訊息到達時,您的應用程式會發出訊號,這是IoHandler負責處理此類事件。換句話說,不要呼叫session.read()。決不。
會話狀態
會話具有狀態,該狀態將在時間內發展。
1.已連線:會話已建立且可用
2.空閒:會話至少在一段時間內沒有處理任何請求(此時間段是可配置的)
(1)空閒讀:實際上沒有讀過一段時間
(2)閒置寫:實際上沒有寫過一段時間
(3)兩者都空閒:一段時間內沒有讀或寫
3. 關閉中:會話正在關閉(剩餘的訊息正在重新整理,清理不會終止)
4. 已關閉:會話現已關閉,無法通過其他方式恢復會話。這實際上不是一個真實的狀態:當會話關閉時,它被刪除。
以下狀態圖公開了所有可能的狀態和轉換:
我們有一組方法來獲取有關會話狀態的一些資訊。
會話狀態:
1.isActive():告訴會話是否有效(根據實現可能意味著不同的事情)
2.isClosing():告訴會話是否已被關閉
3.isConnected():告訴會話是否處於活動狀態(即,不處於關閉模式)
開啟一個會話
實際上,你無需做任何事情:它是自動的!每當遠端對等方連線到伺服器時,伺服器將建立新連線。在客戶端,每次連線到伺服器時,都會建立一個會話。
此會話作為引數傳遞給您的處理程式,以便您可以在應用程式中對其執行某些操作。在客戶端,當您連線到伺服器時,您可以通過以下方式返回建立的會話:
ConnectFuture connectionFuture = connector.connect(address);
connectionFuture.awaitUninterruptibly();
if (!connectionFuture.isConnected()) { return false; } session = connectionFuture.getSession();
您也可以以最短的方式完成:
session = connector.connect(address).getSession();
初始化
建立新會話時,必須對其進行初始化。這是使用預設的IoService配置完成的,您可以稍後更新此配置。實際上,在建立會話時,我們會在內部建立儲存在會話中的預設IoService配置的副本,這是將使用的配置例項(可以修改)。
此初始化還將啟動統計計數器,建立屬性容器,將寫入佇列關聯到會話(這是訊息將被髮送到其中的位置),最終,您是否提供了要執行的特定任務在這個階段,它會呼叫它。
關閉會話
會話可以以4種方式關閉,其中兩種是顯式的:當遠端對等方在發生異常時很好地關閉連線時,呼叫closeNow()方法(顯式)呼叫closeOnFlush()方法(顯式)
(注意,不應再使用兩種不推薦使用的方法:close(boolean)和close())
顯式關閉
前兩個方法可以在應用程式的任何地方呼叫,最大的區別是一個(closeNow())將簡單地關閉會話,丟棄任何等待傳輸給對等方的訊息,而closeOnFlush()將等待任何待處理的訊息已傳輸給同伴。
請注意,如果遠端對等體不再連線,則使用_closeOnFlush()_呼叫關閉的會話將永遠不會被銷燬,除非您還處理其空閒狀態,或者在系統TCP超時關閉套接字之前 - 可能需要幾個小時 - 。始終管理應用程式中的空閒狀態。
遠端對等關閉
當遠端對等方正確關閉會話時,將關閉會話,並且將丟棄所有待處理的訊息。這通常是它的工作方式。
但是,有時候,遠端對等方沒有正確關閉連線(這可能發生在電纜被粗暴地拔掉時)。在這種情況下,會話永遠不會被告知斷開連線。瞭解它的唯一方法是定期檢查會話狀態:如果它的空閒時間超過特定時間 - 必須配置 - ,然後應用程式可以決定關閉會話。否則,當達到TCP超時時,會話將最終關閉(可能需要數小時......)。
異常
在某些情況下,會發生導致會話關閉的例外情況。通常,在建立會話時,我們可能會遇到問題,會話將立即關閉。另一種可能性是我們不能寫一些訊息,例如因為頻道已經關閉:我們然後關閉會話。
總而言之,每當我們在處理會話時遇到異常時,此會話將被關閉。
當然,您的應用程式將通過ExeptionCaught事件通知。
配置
可以為特定會話設定許多不同的引數:
1.接收緩衝區大小
2.傳送緩衝區大小
3.空閒時間
4.寫資料的超時時間
5. ……
加上其他配置,具體取決於使用的傳輸型別(參見第6章 – 傳輸)。
所有這些配置引數都儲存在IoSessionConfig物件中,該物件可以使用session.getConfig()方法從會話中獲取。
有關會話配置的更多資訊,請參閱第4.1章 - 會話配置
管理使用者定義的屬性
可能需要儲存一些可能在以後使用的資料。這是使用與每個會話相關聯的專用資料結構來完成的。這是一個鍵值關聯,可以儲存開發人員可能希望在會話期間保持剩餘的任何型別的資料。
例如,如果要跟蹤使用者自建立會話以來發送的請求數,則可以輕鬆將其儲存到此對映中:只需建立與此值關聯的金鑰即可。
int counterValue = session.getAttribute( "counter" ); session.setAttribute( "counter", counterValue + 1 ); ...
我們有辦法將儲存的屬性處理到會話中:屬性是鍵/值對,可以從會話的容器中新增,刪除和讀取。
建立會話時會自動建立此容器,並在會話終止時將其銷燬。
會話容器
正如我們所說,這個容器是一個鍵/值容器,預設為Map,但如果想要處理長壽命資料,或者如果它們很大則避免將所有這些資料儲存在記憶體中,也可以定義另一個數據結構。 :我們可以實現一個介面和一個工廠,用於在建立會話時建立此容器。
這段程式碼顯示了在會話初始化期間如何建立容器:
protected final void initSession(IoSession session, IoFuture future, IoSessionInitializer sessionInitializer) { ... try { ((AbstractIoSession) session).setAttributeMap(session.getService() .getSessionDataStructureFactory().getAttributeMap(session)); } catch (IoSessionInitializationException e) { throw e; } catch (Exception e) { throw new IoSessionInitializationException( "Failed to initialize an attributeMap.", e); }
如果我們想要定義另一種容器,這裡是我們可以實現的工廠介面:
public interface IoSessionDataStructureFactory { /** * Returns an {@link IoSessionAttributeMap} which is going to be associated * with the specified <tt>session</tt>. Please note that the returned * implementation must be thread-safe. */ IoSessionAttributeMap getAttributeMap(IoSession session) throws Exception; }
會話屬性訪問
有許多方法可用於操作會話的屬性:
boolean containsAttribute(Object key):告訴是否存在給定的屬性
Object getAttribute(Object key):獲取給定屬性的值
Object getAttribute(Object key,Object defaultValue):獲取給定屬性的值,如果不存在則獲取預設值
Set <Object> getAttributeKeys():獲取所有儲存屬性的集合
Object removeAttribute(Object key):刪除給定的屬性
boolean removeAttribute(Object key,Object value):刪除給定的屬性/值對
boolean replaceAttribute(Object key,Object oldValue,Object newValue):替換給定屬性/值對
Object setAttribute(Object key):新增沒有值的新屬性
Object setAttribute(Object key,Object value):新增新的屬性/值對
Object setAttributeIfAbsent(Object key):新增一個沒有值的新屬性(如果它尚不存在)
Object setAttributeIfAbsent(Object key,Object value):新增一個新的屬性/值對,如果它尚不存在
所有這些方法都允許您的應用程式儲存,刪除,獲取或更新儲存在會話中的屬性。另請注意,MINA內部使用了一些屬性:不要輕易修改那些你沒有建立的屬性!
過濾鏈
每個會話都與一系列過濾器相關聯,這些過濾器將在接收或發出傳入請求或傳出訊息時進行處理。這些過濾器是針對每個會話單獨指定的,即使大多數情況下,我們將對所有現有會話使用完全相同的過濾器鏈。
但是,可以動態修改單個會話的鏈,例如通過在鏈中為特定會話新增記錄器篩選器。
統計
每個會話還會記錄會話的內容:
1.接收/傳送的位元組數
2.收到/傳送的訊息數量
3.空閒狀態
4.吞吐量
和許多其他有用的資訊。
有關會話統計資訊的更多資訊,請參閱第4.2章 - 會話統計資訊
處理器Handler
最後,並非最不重要的是,會話附加到Handler,負責將訊息分發給您的應用程式。此處理程式還將通過使用會話傳送迴響應,只需呼叫write()方法:
session.write( <your message> );
第4.1章 - 會話配置
介紹
根據會話的型別,我們可以配置各種元素。其中一些元素在所有會話型別中共享,其他一些是特定的。
我們目前支援4種會話風格:
1.套接字:支援TCP傳輸
2.資料報:支援UDP傳輸
3.Serial:支援RS232傳輸
4.VmPipe:支援IPC傳輸
一般引數
以下是所有全域性引數的列表(可以為任何會話風格設定它們):
引數 |
型別 |
描述 |
預設值 |
idleTimeForBoth |
int |
通知在讀取和寫入時空閒的會話之前等待的秒數 |
無窮 |
idleTimeForRead |
int |
通知讀取空閒的會話之前等待的秒數 |
無窮 |
idleTimeForWrite |
int |
通知寫入時空閒的會話之前等待的秒數 |
無窮 |
maxReadBufferSize |
int |
用於讀取資料的緩衝區的最大大小 |
65536 bytes |
minReadBufferSize |
int |
用於讀取資料的緩衝區的最小大小 |
64 bytes |
readBufferSize |
int |
用於讀取incomming資料的緩衝區的預設大小 |
2048 bytes |
throughputCalculationInterval |
int |
每個吞吐量計算之間的間隔(秒)。 |
3s |
useReadOperation |
boolean |
當我們允許應用程式執行__session.read()_時,標誌設定為TRUE |
FALSE |
writeTimeout |
int |
在挽救寫入操作之前延遲等待完成 |
60s |
可以通過使用getter和setter來訪問所有這些引數(useReadOperation引數getter使用isUseReadOperation()方法)。
套接字特定引數
引數 |
型別 |
描述 |
預設值 |
defaultReuseAddress |
boolean |
SO_REUSEADDR標誌的值 |
true |
keepAlive |
boolean |
SO_KEEPALIVE標誌的值 |
false |
oobInline |
boolean |
SO_OOBINLINE標誌的值 |
false |
receiveBufferSize |
int |
SO_RCVBUF引數的值 |
-1 |
reuseAddress |
boolean |
SO_REUSEADDR標誌的值 |
false |
sendBufferSize |
int |
SO_SNDBUF引數的值 |
-1 |
soLinger |
int |
SO_LINGER引數的值 |
-1 |
tcpNoDelay |
boolean |
TCP_NODELAY標誌的值 |
false |
trafficClass |
int |
IP_TOS引數的值。 IPTOS_LOWCOST(0x02),IPTOS_RELIABILITY(0x04),IPTOS_THROUGHPUT(0x08)或IPTOS_LOWDELAY(0x10)之一 |
0 |
資料報特定引數
引數 |
型別 |
描述 |
預設值 |
|
broadcast |
boolean |
SO_BROADCAST標誌的值 |
false |
|
closeOnPortUnreachable |
boolean |
告訴我們是否應該在埠無法訪問時關閉會話 |
true |
|
receiveBufferSize |
int |
SO_RCVBUF引數的值 |
-1 |
|
reuseAddress |
boolean |
SO_REUSEADDR標誌的值 |
false |
|
sendBufferSize |
int |
SO_SNDBUF引數的值 |
-1 |
|
trafficClass |
int |
IP_TOS引數的值。 IPTOS_LOWCOST(0x02),IPTOS_RELIABILITY(0x04),IPTOS_THROUGHPUT(0x08)或IPTOS_LOWDELAY(0x10)之一 |
0 |
|
序列特定引數
引數 |
型別 |
描述 |
預設值 |
inputBufferSize |
int |
要使用的輸入緩衝區大小 |
8 |
lowLatency |
boolean |
設定低延遲模式 |
false |
outputBufferSize |
int |
要使用的輸出緩衝區大小 |
8 |
receiveThreshold |
int |
以位元組為單位設定接收閾值(將其設定為-1表示禁用) |
-1 |
第4.2章 - 會話統計
我們會在每個會話中保留一些有關正在發生的事情的統計資料。並非所有這些統計資料都是通過每條訊息計算出來的:其中一些是按需計算的。
引數 |
型別 |
描述 |
預設值 |
readBytes |
long |
自會話建立以來讀取的總位元組數 |
yes |
readBytesThroughput |
double |
最後一個時間間隔內每秒讀取的位元組數 |
no |
readMessages |
long |
自會話建立以來讀取的訊息總數 |
yes |
readMessagesThroughput |
double |
上一個時間間隔內每秒讀取的訊息數 |
no |
scheduledWriteBytes |
AtomicInteger |
等待寫入的位元組數 |
yes |
scheduledWriteMessages |
AtomicInteger |
等待寫入的訊息數 |
yes |
writtenBytes |
long |
自會話建立以來寫入的總位元組數 |
yes |
writtenBytesThroughput |
double |
最後一個時間間隔內每秒寫入的位元組數 |
no |
writtenMessages |
long |
自會話建立以來寫入的訊息總數 |
yes |
writtenMessagesThroughput |
double |
在最後一個時間間隔內每秒寫入的訊息數 |
no |
可以使用getter讀取所有這些引數。