Reactor 模型¶
传统服务设计[1]¶
Each thread synchronously blocks in the accept socket call waiting for a client connection request;[1^]
A client connects to the server, and the connection is accepted;
The new client’s HTTP request is synchronously read from the network connection;
The request is parsed;
The requested file is synchronously read;
The file is synchronously sent to the client.
这种设计的缺点:
可伸缩性: 为每个请求分配一个线程,高负载下容易导致资源耗尽(可以使用线程池技术缓解此问题)。
资源利用: 线程在等待 IO 时会完全阻塞,导致资源利用率低。
通吐量低: 频繁的线程切换,使其处理大量小型请求时吞吐量受限。
Reactor[1]¶
连接¶
The Web Server registers an Acceptor with the Initiation Dispatcher to accept new connections;
The Web Server invokes event loop of the Initiation Dispatcher;
A client connects to the Web Server;
The Acceptor is notified by the Initiation Dispatcher of the new connection request and the Acceptor accepts the new connection;
The Acceptor creates an HTTP Handler to service the new client;
HTTP Handler registers the connection with the Initiation Dispatcher for reading client request data (that is, when the connection becomes “ready for reading”);
The HTTP Handler services the request from the new client.
读写¶
The client sends an HTTP GET request;
The Initiation Dispatcher notifies the HTTP Handler when client request data arrives at the server;
The request is read in a non-blocking manner such that the read operation returns EWOULDBLOCK if the operation would cause the calling thread to block (steps 2 and 3 repeat until the request has been completely read);
The HTTP Handler parses the HTTP request;
The requested file is synchronously read from the file system;
The HTTP Handler registers the connection with the Initiation Dispatcher for sending file data (that is, when the connection becomes “ready for writing”);
The Initiation Dispatcher notifies the HTTP Handler when the TCP connection is ready for writing;
The HTTP Handler sends the requested file to the client in a non-blocking manner such that the write operation returns EWOULDBLOCK if the operation would cause the calling thread to block (steps 7 and 8 will repeat until the data has been delivered completely).
Reactor 单线程[3]¶
一般不会这样子使用。
Reactor 使用线程池[3]¶
这就是 netty 中 bossGroup
和 workerGroup
使用同一个 EventLoopGroup 的模式。
1 EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); // (1)
2 ServerBootstrap server = new ServerBootstrap(); // (2)
3 server.group(eventLoopGroup)
4 .channel(NioServerSocketChannel.class)
5 //...
多个 Reactor¶
这其是就是 netty 中 bossGroup
和 workerGroup
,一个专门用来查询客户端的连接事件,一个专门用来查询连接的读写事件。
1 EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
2 EventLoopGroup workerGroup = new NioEventLoopGroup();
3 ServerBootstrap server = new ServerBootstrap(); // (2)
4 server.group(bossGroup, workerGroup)
5 .channel(NioServerSocketChannel.class) // (3)
6 .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
7 @Override
8 public void initChannel(SocketChannel ch) throws Exception {
9
10 }
11 })
12 .option(ChannelOption.SO_BACKLOG, 128) // (5)
13 .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
Proactor[2]¶
经典服务设计使用的是阻塞IO,不能有效的利用资源,Reactor 服务设计使用 IO 多路复用,是目前使用最广泛的模式,也有进一步的优化设计——Proactor,但目前使用并不广泛,主要是因为它依赖于操作系统提供的异步 IO,而且不太符合人类直觉,基于它设计的程序理解起来比较困难。(Linux 中 的 io_uring 异步 io 估计会流性起来 )。