网络编程中出了 IO 模型之外,另一个被经常提到的就是 Reactor 模型和 Proactor 模型, 其实这两个模型和 IO 模型有着重要的关系,看完本文你就知道了.
如果你对 IO 模型还不清楚,请先移步下面的文章后再回头来看本文.《到底什么是IO》https://mp.weixin.qq.com/s/OkIajg8aDlkLtsE81NP5jQ
在网络编程中,服务器设计的好坏直接影响到能支持的最大并发数,通常会考虑两种方式,一种是通过 线程的方式,另一种就是通过事件的方式.
单线程网络通信中, 客户端向服务器请求数据时,服务器通常有两种方式处理,一种就是单线程,这样做的话所有的客户端都需要排队等待, 如果服务端处理逻辑比较简单快速的话, 针对少量的客户端还是可以的,但是一旦客户端数量增加,服务端处理时间会线性增加, 如果处理逻辑复杂并且还涉及到 IO ,简直不可想象.
于是多线程模型出来了.
多线程为了解决但线程带来的处理慢,延迟大的问题,引入了多线程. 服务器没收到一个客户端的连接就分配一个线程来处理, 当然分配的方式你可以选择 new 也可以选择线程池的方式,通常为了避免反复创建导致的资源浪费和性能低下问题而采用线程池的方式.
看似完美了,但是如果某些客户端连接在处理时比较耗时(由于各种原因,比如网络慢、数据量大处理复杂等),就会长期占用线程,导致其他客户端连接没办法获取线程处理.
事件模式多线程的方式看似完美,但是有个问题就是, 线程利用率不高, 每来一个连接就分配一个线程,可能这个连接压根就没发数据,这岂不是白占着坑位吗?
基于事件模式就是连一个连接,服务端并不会立刻分配线程,而是监听事件(读、写、请求连接),有事件再分配线程进行处理.在实际工程中通常会将事件分发器(dispatcher)、处理器(handler)分开设计.
Reactorreactor “反应堆”, 对于所有来自客户端的事件作出反应,其实就是基于 NIO 的多路复用实现的一种方式.
根据 dispatcher、handler 线程模型可以分为 4种: 单 dispatcher 单 handler, 单 dispatcher 多 handler, 多 dispatcher 单 handler 以及多 dispatcher 多 handler, 但是 多 dispatcher 单 handler 对性能提升并没有太大价值,所以下面就讨论 3 种.
1. 单 dispatcher 单 handler
(1) 客户端连接过来, dispatcher 通过 select 轮询事件
(2) 如果是连接事件,交由 AcceptHandler 处理, 并向 select 注册 ready for reading(可读) 事件到 BusinessHandler
(3) 如果是 ready for reading 事件, 交由 BusinessHandler 处理,并返回结果
如果业务处理比较耗时的话,就会因为处理不过来而导致延迟.
2. 单 dispatcher 多 handler
工作过程类似,只是在收到 ready for reading 事件时, 交由一个单独的线程使用 BusinessHandler 处理.业务处理能力提高了,但是面对高并发时, 单线程的 dispatcher 成了瓶颈, 需要分发所有的事件.
3. 多 dispatcher 多 handler
也叫主从 Reactor 模式, 主线程中的 Dispatcher 只负责处理连接事件, 将成功的连接传给从 Reactor, 从 Reactor 处理调用业务处理器处理读写事件, 业务处理器又使用 worker 线程池进行业务处理.
变种:
上图中 BusinessHandler 只负责读写调用,具体的业务处理在线程池中去做, 业务处理完后还得返回给 BusinessHandler 返回给客户端,涉及到数据共享问题,于是出现了变种:
所有的业务处理,包括 read 、业务处理、write 都在BusinessHandler 中的线程池中操作.
Netty 就是基于 reactor 的多 dispatcher 多handler 实现的.
Proactor先看下 wiki 中的定义:
Proactor is a software design pattern for event handling in which long running activities are running in an asynchronous part. A completion handler is called after the asynchronous part has terminated. The proactor pattern can be considered to be an asynchronous variant of the synchronous reactor pattern.
Proactor 是一种用于事件处理的软件设计模式,其中长时间运行的活动(IO)在异步部分运行。异步部分终止后,将调用完成处理程序。前置器模式可以被认为是同步 reactor 模式的异步变体。
上面的图只是介绍了 proactor 的工作原理,实际编写代码时,可能非常简单, 比如 Java 中:
AsynchronousServerSocketChannel ssc = AsynchronousServerSocketChannel.open();
// Bind the server socket to the local host and port
InetAddress lh = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(lh, port);
ssc.bind(isa);
ssc.accept(this, new CompletionHandler() {
@Override
public void completed(AsynchronousSocketChannel result, AIOTimeServer attachment) {
// 读取客户端数据
ByteBuffer byteBuffer = ByteBuffer.allocate(2000);
result.read(byteBuffer);
String responsedocument = ("" +
"AIO Server: " + System.currentTimeMillis() + "");
String responseHeader = ("HTTP/1.1 200 OKrn" +
"Content-Type: text/html; charset=UTF-8rn" +
"Content-Length: " + responsedocument.length() +
"rnrn");
try {
// 响应客户端数据
result.write(encoder.encode(CharBuffer.wrap(responseHeader)));
result.write(encoder.encode(CharBuffer.wrap(responsedocument)));
} catch (IOException e) {
e.printStackTrace();
}
try {
// 关闭连接
result.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, AIOTimeServer attachment) {
System.out.println("failed");
}
});
所以 Proactor 也是基于事件处理的软件设计模式, 对 OS 的数据ready 事件作出反应, 所以依赖于操作系统的 AIO , windows 对 AIO 支持较好, POSIX 中已经定义了接口 aio_read/aio_write , 但是Linux 中一直没有实现, 直到 2.6 才真正实现可能还不完美.
如果觉得还不错的话,关注、分享、在看, 原创不易,且看且珍惜~



