生活随笔
收集整理的這篇文章主要介紹了
reactor线程模型_从TCP服务器到I/O模型,带你学习Netty
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
學習Netty就不得不從TCP服務器和I/O模型說起,了解TCP服務器架構和I/O模型的演進有助于深入了解Netty。
TCP服務器的架構
一般地,TCP服務器有兩種套接字,監聽套接字和已連接套接字。監聽套接字用于TCP的監聽,一旦連接建立便產生已連接套接字,服務器利用已連接套接字與客戶端進行通信。
- 迭代服務器
- 在迭代服務器中,監聽套接字會一直阻塞直到能夠接受連接,接受連接后利用已連接套接字與客戶端通信,這些工作都是在同一個線程中完成的,示意Java代碼如下。這種模式是串行處理,很難應對并發量較大的情況。
try (ServerSocket serverSocket = new ServerSocket(port)) { while (true) { Socket socket = serverSocket.accept(); // ... }} catch (IOException e) { e.printStackTrace();}
- 并發服務器
- 在并發服務器中,監聽套接字會一直阻塞直到能夠接受連接,接受連接后,服務器會讓子線程/進程去處理已連接套接字,示意Java代碼如下。這種模式雖然是并行處理,可以不干擾服務端的監聽,但是由于每次新來一個請求就會產生一個新的線程去處理,出于資源的考慮很難應對高并發的情況。
try (ServerSocket serverSocket = new ServerSocket(port)) { while (true) { final Socket socket = serverSocket.accept(); new Thread(() -> { // ... }).start(); }} catch (IOException e) { e.printStackTrace();}
- IO多路復用(事件驅動)
- 為了能在一個線程中處理多個連接,可以使用IO多路復用(事件驅動),典型的有Linux C中的select、poll和epoll,Java的Selector類等。以Selector為例,調用者在選擇器上為不同的連接注冊自己感興趣的事件(可讀/可寫/可接受/可連接),然后阻塞在select上,當事件發生時調用者便會得到通知,并且知道是哪個連接觸發了事件,以便可以進一步處理。
- Selector實現的HTTP服務器示意如下:
public class NioServer { private static final int BUFFER_SIZE = 512; private static final String HTTP_RESPONSE_BODY = "Hello wolrd"; private static final String HTTP_RESPONSE_HEADER = "HTTP/1.1 200" + "Content-Type: text/html" + "Content-Length: " + HTTP_RESPONSE_BODY.length() + ""; private static final String HTTP_RESPONSE = HTTP_RESPONSE_HEADER + HTTP_RESPONSE_BODY; // IO多路復用 public void selector(int port) { try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); Selector selector = Selector.open()) { serverSocketChannel.configureBlocking(false); serverSocketChannel.bind(new InetSocketAddress(port)); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { int readyChannels = selector.select(); if (readyChannels == 0) { continue; } Set selectedKeys = selector.selectedKeys(); Iterator iter = selectedKeys.iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); if (key.isAcceptable()) { SocketChannel channel = ((ServerSocketChannel) key.channel()).accept(); System.out.println("accept: " + channel); channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ); } if (key.isReadable()) { SocketChannel channel = (SocketChannel) key.channel(); System.out.println("read: " + channel); ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); int bytesRead = channel.read(buffer); while (bytesRead > 0) { buffer.flip(); while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } buffer.clear(); bytesRead = channel.read(buffer); } ByteBuffer writeBuf = ByteBuffer.wrap(HTTP_RESPONSE.getBytes()); while (writeBuf.hasRemaining()) { channel.write(writeBuf); } channel.close(); } iter.remove(); } } } catch (IOException e) { e.printStackTrace(); } } }
I/O模型
一個輸入操作通常包括兩個不同的階段[1][2]:
等待數據準備好從內核向進程復制數據(1) 阻塞式I/O模型
(2) 非阻塞式I/O模型
(3) I/O復用模型
(4) 信號驅動式I/O模型
(5) 異步I/O模型
同步I/O和異步I/O對比
POSIX把這兩個術語定義如下:
- 同步I/O操作導致請求進程阻塞,直至I/O操作完成;
- 異步I/O操作不導致請求進程阻塞。
根據上述定義,前4種模型——阻塞式I/O模型、非阻塞式I/O模型、I/O復用模型和信號驅動式I/O模型都是同步I/O模型,因為其中真正的I/O操作(recvfrom)將阻塞進程。只有異步I/O模型與POSIX定義的異步I/O相匹配。
Netty
Netty是一款異步的事件驅動的網絡應用編程框架,支持快速地開發可維護的高性能的面向協議的服務器和客戶端。與使用阻塞I/O來處理大量事件相比,使用非阻塞I/O來處理更快速、更經濟,Netty使用了Reactor模式將業務和網絡邏輯解耦,實現關注點分離[3]。
Reactor模式
Reactor模式(反應堆模式)是一種處理一個或多個客戶端并發交付服務請求的事件設計模式。當請求抵達后,服務處理程序使用I/O多路復用策略,然后同步地派發這些請求至相關的請求處理程序[4]。
Reactor模式中的角色:
- Reactor:監聽端口,響應與分發事件;
- Acceptor:當Accept事件到來時Reactor將Accept事件分發給Acceptor,Acceptor將已連接套接字的通道注冊到Reactor上;
- Handler:已連接套接字做業務處理。
單Reactor單線程
在這種模式中,Reactor、Acceptor和Handler都運行在一個線程中。
單Reactor多線程
在這種模式中,Reactor和Acceptor運行在同一個線程,而Handler只有在讀和寫階段與Reactor和Acceptor運行在同一個線程,讀寫之間對數據的處理會被Reactor分發到線程池中。
多Reactor多線程
在這種模式中,主Reactor負責監聽,與Acceptor運行在同一個線程,Acceptor會將已連接套接字的通道注冊到從Reactor上,從Reactor負責響應和分發事件,起到類似多線程Reactor的作用。Netty服務端使用了該種模式。
總結
以上是生活随笔為你收集整理的reactor线程模型_从TCP服务器到I/O模型,带你学习Netty的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。