1. 程式人生 > >JavaNIO Java進階知識點5:服務端高併發的基石 - NIO與Reactor模式以及AIO與Proactor模式

JavaNIO Java進階知識點5:服務端高併發的基石 - NIO與Reactor模式以及AIO與Proactor模式

javaNIO對於多路複用io(同步非阻塞io)的實現

package test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public
class NioPractice { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub //開啟一個ServerSocketChannel在埠8080監聽 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress("localhost", 8080));
//開啟一個多路複用器,可以實現用一個執行緒監控所有IO連線的IO就緒事件 Selector selector=Selector.open(); //將severSocketChannel註冊到多路複用器上,並宣告關注其accept就緒事件 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while(selector.select()!=0) { Iterator<SelectionKey> iterator=selector.selectedKeys().iterator();
while(iterator.hasNext()) { SelectionKey key=iterator.next(); if(key.isReadable()) { //將Channel裡的資料讀出,當有大量資料需要讀取時,導致監控執行緒一直為這個io服務, //因此可以專門開一個執行緒池來對io讀寫處理(不由監控執行緒來處理,由新開的執行緒池來處理,所以監控執行緒可以去處理別的就緒事件) } if(key.isWritable()) { } if(key.isAcceptable()) { //統一由selector多路複用器來監聽,當socketChannel為非阻塞時,省去了單獨判斷它是否成功接受連線 SocketChannel socketChannel=serverSocketChannel.accept(); //將socketChannel註冊到多路複用器上,並宣告關注其read事件 serverSocketChannel.register(selector, SelectionKey.OP_READ); } iterator.remove(); } } } }

基於 Java NIO 的事件分離模式

採用 Java NIO 技術實現的典型的兩種高效、應用廣泛的事件分離模式:一個是反應器(Reactor)模式,一個是前攝器(Proactor)模式。

 同步非阻塞io

反應器(Reactor)模式目的是使伺服器具有併發處理服務請求的能力。伺服器首先註冊多個事件處理器(Event Handler),事件分離器(Event Demultiplexer)負責接收一個或者多個客戶端發來的請求,並將事件分發到對應的事件處理器上。

一個典型的讀操作的過程是:

1)  伺服器程式註冊 Accept 事件,事件分離器等待客戶端的連線請求;

2)  客戶端請求到來後,將連線事件分發到 Acceptor 事件處理器中,建立連線。

3)  連線建立完成後伺服器程式再註冊一個讀操作事件。事件分離器繼續等待事件發生;

4)  當讀操作在對應的連線上就緒後,事 件分離器分發事件到對應的Read Handler 事件處理器中基於同步 I/O 進行阻塞讀取;

5)  讀取完成後再根據業務需要做進一步處理。 整個過程是由伺服器感興趣的事件觸發的,因此形象的叫做反應器模式。


 非同步非阻塞io

前攝器(Proactor)模式也是一種事件分離模式,與 Reactor 不同的是基於非同步 I/O 的,實現了真正的無阻塞非同步的 I/O 操作。前攝器模式同樣是分離事件給對應的事件處理器,然而這裡的事件不再是就緒事件,如就緒讀或者就緒寫,而是非同步操作的完成事件。

一個典型的讀操作處理過程如下:

1)  伺服器程式註冊 Accept 事件,事件分離器等待客戶端的連線請求;

2)  客戶端請求到來後,將連線事件分發到 Acceptor 事件處理器中,建立連線。

3)  連線建立完成後伺服器程式再註冊一個讀操作完成事件。事件分離器繼續讀操作完成事件的發生;

4)  當讀操作在對應的連線上就緒後,由作業系統觸發一個非同步讀取操作,等讀取完成後,會將讀取內容放在使用者指定的緩衝區,並通知應用程式事件完成。

5)  事件分離器接收到完成事件後,將事件分發到 ReadHandler 事件處理器中直接從指定緩衝區中取出資料,而不需要進行實際的 I/O 操作。

6)  緩衝區資料取出後再根據不同的業務做進一步的處理

 

典型的非同步模式實現,都建立在作業系統支援非同步 API 的基礎之上,這種實現稱為系統級非同步,因為應用程式完全依賴作業系統執行真正的 I/O 工作 

 

這兩個模式都使用了事件驅動模型:一個是在資料準備好時通知事件處理器去進行io處理,一個 是在已經完成了io處理後通知事件處理器進行業務處理

事件驅動模型圖

使用JavaAIO對檔案進行非同步讀寫的實現如下:

package test;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class AIOtest1 {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        Path path = Paths.get("test-write.txt");
        if(!Files.exists(path)){
            Files.createFile(path);
        }
        AsynchronousFileChannel fileChannel = 
            AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        long position = 0;

        buffer.put("test data".getBytes());
        buffer.flip();
        
        fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {

            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                System.out.println("bytes written: " + result);
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                System.out.println("Write failed");
                exc.printStackTrace();
            }
        });

    }

}

參考:

論文:基於Netty的高可服務訊息中介軟體的研究與實現_崔曉旻

部落格:Java進階知識點5:服務端高併發的基石 - NIO與Reactor模式以及AIO與Proactor模式

Java NIO AsynchronousFileChannel