netty學習(一)簡述NIO

NIO簡介

NIO,New IO(官方),Non-blocking IO(非官方),是 JDK1.4 中引入的一種新的 IO 標準,是一種同步非阻塞 IO。NIO 是以塊爲單位進行數據處理的,當然塊的大小是程序員自己指定的。其相對於 BIO 的以字節/字符爲單位所進行的阻塞式處理方式,大大提高了讀寫效率與併發度。

  • BIO:Blocking IO,同步阻塞 IO
  • NIO:Non-blocking IO,同步非阻塞 IO JDK1.4
  • AIO:異步非阻塞 IO,也稱爲 NIO2.0 JDK1.7

爲什麼NIO適合高併發的場景

首先看下面這張BIO的客戶端與服務器線程之間的示意圖
在這裏插入圖片描述
有圖可知,每一個客戶端與服務器建立鏈接就會產生一個線程,而線程資源是非常寶貴的,而且是有上線的,所以就造成了BIO併發處理的瓶頸。而NIO爲了解決這一點使用了下面的模型
在這裏插入圖片描述

NIO中Channel與線程之間是多對一的關係。而哪一個Channel使用線程是通過selector這一多路複用器來決定的。當一個Channel準備就緒時就通知線程來處理該通道的請求。由於多個Channel對應一個線程,所以NIO是可以處理高併發的。

JAVA NIO中重要的API

java.nio.channels.spi.SelectorProvider

該類主要負責提供選擇器以及可以選擇的Channel。
該類通過provider()維護了一個全局且唯一的一個SelectorProvider實例,而且該類的所有方法都是線程安全的。

    /**
     * Returns the system-wide default selector provider for this invocation of
     * the Java virtual machine.
     *
     * <p> The first invocation of this method locates the default provider
     * object as follows: </p>
     *
     * <ol>
     *
     *   <li><p> If the system property
     *   <tt>java.nio.channels.spi.SelectorProvider</tt> is defined then it is
     *   taken to be the fully-qualified name of a concrete provider class.
     *   The class is loaded and instantiated; if this process fails then an
     *   unspecified error is thrown.  </p></li>
     *
     *   <li><p> If a provider class has been installed in a jar file that is
     *   visible to the system class loader, and that jar file contains a
     *   provider-configuration file named
     *   <tt>java.nio.channels.spi.SelectorProvider</tt> in the resource
     *   directory <tt>META-INF/services</tt>, then the first class name
     *   specified in that file is taken.  The class is loaded and
     *   instantiated; if this process fails then an unspecified error is
     *   thrown.  </p></li>
     *
     *   <li><p> Finally, if no provider has been specified by any of the above
     *   means then the system-default provider class is instantiated and the
     *   result is returned.  </p></li>
     *
     * </ol>
     *
     * <p> Subsequent invocations of this method return the provider that was
     * returned by the first invocation.  </p>
     *
     * @return  The system-wide default selector provider
     */
    public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

後面不管是取得通道還是選擇器都是通過這個全局唯一的provider實例來獲取的。

    /**
     * Opens a selector.
     *
     * <p> The new selector is created by invoking the {@link
     * java.nio.channels.spi.SelectorProvider#openSelector openSelector} method
     * of the system-wide default {@link
     * java.nio.channels.spi.SelectorProvider} object.  </p>
     *
     * @return  A new selector
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }
    /**
     * Opens a server-socket channel.
     *
     * <p> The new channel is created by invoking the {@link
     * java.nio.channels.spi.SelectorProvider#openServerSocketChannel
     * openServerSocketChannel} method of the system-wide default {@link
     * java.nio.channels.spi.SelectorProvider} object.
     *
     * <p> The new channel's socket is initially unbound; it must be bound to a
     * specific address via one of its socket's {@link
     * java.net.ServerSocket#bind(SocketAddress) bind} methods before
     * connections can be accepted.  </p>
     *
     * @return  A new socket channel
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public static ServerSocketChannel open() throws IOException {
        return SelectorProvider.provider().openServerSocketChannel();
    }

java.nio.channels.Selector

  • 該類在NIO中充當的是一個多路複用器的角色。
  • 這個類創建有兩種方法,一種是Selector的open()方法,一種是SelectorProvider的openSelector()方法,不過第一種方式本質上也是調用的SelectorProvider的openSelector()方法。
  • 選擇器被open之後會一直處於open狀態,直到調用了Selector的close()方法。
  • 可選擇通道是通過SelectionKey的方式向選擇器的註冊的。一個通道對應一個SelectionKey。
  • Selector一共維護了三個SelectionKey的集合,分別爲所有的Key的集合(key set),所有的準備就緒的Key的集合(selected-key set),處理完畢的待從selector中刪除的key的集合(cancelled-key set)。在下一次選擇時會把這裏面的Key從所有Key集合中刪除掉。
  • key set元素的添加是在Channel註冊到Selector時,刪除是在每次選擇操作時將包含在cancelled-key set中的元素刪除。注意該集合本身是不會直接進行修改的
  • selected-key set中的元素添加是在Selector進行選擇操作時添加的,並且永遠不會自動移除,需要你手動調用set集合的remove方法或者Set集合Iterator的remove方法進行移除,也就是說在進行業務處理之後你要手動調用remove方法來避免重複的操作。
  • cancelled-key set中的元素在調用SelectionKey的cancle方法時添加,在下一次選擇操作時從該集合以及 key set對應的元素進行刪除。
  • Selector的選擇操作select()採用了poll輪詢的方式挨個查詢是否有準備就緒的Channel期間執行線程是阻塞狀態,當然你可以指定阻塞超時時間,來結束阻塞狀態。該方法返回值有可能爲0,代表該階段沒有準備就緒的通道。選擇操作會對三個元素集合產生影響。會刪除cancelled-key set中的元素以及key set中對應的元素,會將準備就緒的通道放入selected-key set中。

java.nio.channels.SelectionKey

  • 每一個Channel註冊至Selector中都會產生一個SelectionKey。
  • 一個SelectionKey的取消有三種方式,SelectionKey的取消操作,通道的關閉,以及Selector關閉。
  • 一個SelectionKey包含一個或者多個感興趣的操作,即Selector輪詢時監聽的事件。包括OP_READ(有消息到達可以被讀取)。OP_WRITE(可以被寫入)。OP_CONNECT(套接字通道已經準備好完成它的連接)。OP_ACCEPT(有通道進行接入)

NIO服務端處理消息的流程

  1. 打開一個服務端的通道,並選擇監聽的端口。
  2. 打開一個Selecor
  3. 將通道註冊到Selector中,並選擇需要監聽的事件
  4. Selector以輪詢的方式查找是否有準備就緒的通道,並將該通道的SelectionKey放入selected-key set中。
  5. 將處理過的通道的key從selected-key set中移除
  6. 繼續第4步直到Channel或者Selector關閉
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章