Apache Mina學習1

Apache MINA 2 是一個開發高性能和高可伸縮性網絡應用程序的網絡應用框架。它提供了一個抽象的事件驅動的異步 API,可以使用 TCP/IP、UDP/IP、串口和虛擬機內部的管道等傳輸方式。Apache MINA 2 可以作爲開發網絡應用程序的一個良好基礎。本文將介紹 Apache MINA 2 的基本概念和 API,包括 I/O 服務、I/O 會話、I/O 過濾器和 I/O 處理器。另外還將介紹如何使用狀態機。本文包含簡單的計算器服務和複雜的聯機遊戲兩個示例應用。

Apache MINA 2 是一個開發高性能和高可伸縮性網絡應用程序的網絡應用框架。它提供了一個抽象的事件驅動的異步 API,可以使用 TCP/IP、UDP/IP、串口和虛擬機內部的管道等傳輸方式。Apache MINA 2 可以作爲開發網絡應用程序的一個良好基礎。下面將首先簡單介紹一下 Apache MINA 2。

Apache MINA 2 介紹

Apache MINA 是 Apache 基金會的一個開源項目,目前最新的版本是 2.0.0-RC1。本文中使用的版本是 2.0.0-M6。從 參考資料 中可以找到相關的下載信息。下面首先介紹基於 Apache MINA 的網絡應用的一般架構。

基於 Apache MINA 的網絡應用的架構

基於 Apache MINA 開發的網絡應用,有着相似的架構。圖 1 中給出了架構的示意圖。


圖 1. 基於 Apache MINA 的網絡應用的架構
基於 Apache MINA 的網絡應用的架構 

如 圖 1 所示,基於 Apache MINA 的網絡應用有三個層次,分別是 I/O 服務、I/O 過濾器和 I/O 處理器:

  • I/O 服務:I/O 服務用來執行實際的 I/O 操作。Apache MINA 已經提供了一系列支持不同協議的 I/O 服務,如 TCP/IP、UDP/IP、串口和虛擬機內部的管道等。開發人員也可以實現自己的 I/O 服務。
  • I/O 過濾器:I/O 服務能夠傳輸的是字節流,而上層應用需要的是特定的對象與數據結構。I/O 過濾器用來完成這兩者之間的轉換。I/O 過濾器的另外一個重要作用是對輸入輸出的數據進行處理,滿足橫切的需求。多個 I/O 過濾器串聯起來,形成 I/O 過濾器鏈。
  • I/O 處理器:I/O 處理器用來執行具體的業務邏輯。對接收到的消息執行特定的處理。
創建一個完整的基於 Apache MINA 的網絡應用,需要分別構建這三個層次。Apache MINA 已經爲 I/O 服務和 I/O 過濾器提供了不少的實現,因此這兩個層次在大多數情況下可以使用已有的實現。I/O 處理器由於是與具體的業務相關的,一般來說都是需要自己來實現的。

 

事件驅動的 API

Apache MINA 提供的是事件驅動的 API。它把與網絡相關的各種活動抽象成事件。網絡應用只需要對其感興趣的事件進行處理即可。事件驅動的 API 使得基於 Apache MINA 開發網絡應用變得比較簡單。應用不需要考慮與底層傳輸相關的具體細節,而只需要處理抽象的 I/O 事件。比如在實現一個服務端應用的時候,如果有新的連接進來,I/O 服務會產生 sessionOpened這樣一個事件。如果該應用需要在有連接打開的時候,執行某些特定的操作,只需要在 I/O 處理器中此事件處理方法 sessionOpened中添加相應的代碼即可。

在介紹 Apache MINA 中的基本概念的細節之前,首先通過一個簡單的應用來熟悉上面提到的三個層次的具體職責。




回頁首


從簡單應用開始

在使用 Apache MINA 開發複雜的應用之前,首先將介紹一個簡單的應用。通過此應用可以熟悉上面提到的三個層次,即 I/O 服務、I/O 過濾器和 I/O 處理器。該應用是一個簡單的計算器服務,客戶端發送要計算的表達式給服務器,服務器返回計算結果。比如客戶端發送 2+2,服務器返回 4.0作爲結果。

在實現此計算器的時候,首先需要考慮的是 I/O 服務。該計算器使用 TCP/IP 協議,需要在指定端口監聽,接受客戶端的連接。Apache MINA 提供了基於 Java NIO 的套接字實現,可以直接使用。其次要考慮的是 I/O 過濾器。I/O 過濾器過濾所有的 I/O 事件和請求,可以用來處理橫切的需求,如記錄日誌、壓縮等。最後就是 I/O 處理器。I/O 處理器用來處理業務邏輯。具體到該應用來說,就是在接收到消息之後,把該消息作爲一個表達式來執行,並把結果發送回去。I/O 處理器需要實現 org.apache.mina.core.service.IoHandler接口或者繼承自org.apache.mina.core.service.IoHandlerAdapter。該應用的 I/O 處理器的實現如 清單 1 所示。


清單 1. 計算器服務的 I/O 處理器 CalculatorHandler 
public class CalculatorHandler extends IoHandlerAdapter { 
    private static final Logger LOGGER = LoggerFactory 
        .getLogger(CalculatorHandler.class); 

    private ScriptEngine jsEngine = null; 

    public CalculatorHandler() { 
        ScriptEngineManager sfm = new ScriptEngineManager(); 
        jsEngine = sfm.getEngineByName("JavaScript"); 
        if (jsEngine == null) { 
            throw new RuntimeException("找不到 JavaScript 引擎。"); 
        } 
    } 

    public void exceptionCaught(IoSession session, Throwable cause) 
        throws Exception { 
        LOGGER.warn(cause.getMessage(), cause); 
    } 

    public void messageReceived(IoSession session, Object message) 
        throws Exception { 
        String expression = message.toString(); 
        if ("quit".equalsIgnoreCase(expression.trim())) { 
            session.close(true); 
            return; 
        } 
        try { 
            Object result = jsEngine.eval(expression); 
            session.write(result.toString()); 
        } catch (ScriptException e) { 
            LOGGER.warn(e.getMessage(), e); 
            session.write("Wrong expression, try again."); 
        } 
    } 
}

在 清單 1 中,messageReceived 由 IoHandler 接口聲明。當接收到新的消息的時候,該方法就會被調用。此處的邏輯是如果傳入了“quit”,則通過 session.close關閉當前連接;如果不是的話,就執行該表達式並把結果通過 session.write發送回去。此處執行表達式用的是 JDK 6 中提供的 JavaScript 腳本引擎。此處使用到了 I/O 會話相關的方法,會在下面進行說明。

接下來只需要把 I/O 處理器和 I/O 過濾器配置到 I/O 服務上就可以了。具體的實現如 清單 2 所示。


清單 2. 計算器服務主程序 CalculatorServer
public class CalculatorServer { 
    private static final int PORT = 10010; 

    private static final Logger LOGGER = LoggerFactory 
        .getLogger(CalculatorServer.class); 

    public static void main(String[] args) throws IOException { 
        IoAcceptor acceptor = new NioSocketAcceptor(); 

        acceptor.getFilterChain().addLast("logger", new LoggingFilter()); 
        acceptor.getFilterChain().addLast( 
            "codec", 
            new ProtocolCodecFilter(new TextLineCodecFactory(Charset 
                .forName("UTF-8")))); 

        acceptor.setHandler(new CalculatorHandler()); 
        acceptor.bind(new InetSocketAddress(PORT)); 

        LOGGER.info("計算器服務已啓動,端口是" + PORT); 
    } 
}

清單 2 中,首先創建一個 org.apache.mina.transport.socket.nio.NioSocketAcceptor 的實例,由它提供 I/O 服務;接着獲得該 I/O 服務的過濾器鏈,並添加兩個新的過濾器,一個用來記錄相關日誌,另外一個用來在字節流和文本之間進行轉換;最後配置 I/O 處理器。完成這些之後,通過 bind 方法來在特定的端口進行監聽,接收連接。服務器啓動之後,可以通過操作系統自帶的 Telnet 工具來進行測試,如 圖 2 所示。在輸入表達式之後,計算結果會出現在下面一行。


圖 2. 使用 Telnet 工具測試計算器服務
使用 Telnet 工具測試計算器服務 

在介紹了簡單的計算器服務這個應用之後,下面說明本文中會使用的複雜的聯機遊戲應用。

本文章轉自:https://www.ibm.com/developerworks/cn/java/j-lo-mina2/#code1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章