我们都说 Netty 是一款基于异步事件驱动来设计和实现的高性能IO框架,它之所以高性能,重要的原因之一是其线程模型的设计,Netty 的线程模型是基于 Reactor 设计模式的,它主要包含两个线程池:一个是 Boss 线程池,另一个是 Worker 线程池。Boss 线程池主要负责接受客户端连接请求,并将连接请求注册到 Worker 线程池中的某个线程的 Selector 上。Boss 线程池通常只有一个线程(如果建链也成为瓶颈,那么Boss线程池也可以有多个)。Worker 线程池主要负责处理客户端连接请求,并进行网络 I/O 操作。Worker 线程池的大小通常是根据 CPU 核数和业务需求来调整的。
Reactor 设计模式最早由 Doug Schmidt 和 Steve Vinoski 在 1995 年的一篇论文《Reactor: An Object Behavioral Pattern for Concurrent Handling of Multiple Event-based Requests》中提出,这是一篇关于并发编程模式的经典论文。这篇论文对基于 Reactor 设计模式的网络编程框架进行了探讨。Reactor 设计模式是一种用于处理事件驱动 I/O 操作的设计模式,它主要包含以下两个核心组件:
- Reactor:负责监听事件和分发事件,它将事件分发给对应的 Handler 处理器。
- Handler:负责处理具体的事件,例如读取数据、解析数据、处理业务逻辑、发送响应数据等等。
Reactor 设计模式的基本思想是:当有事件发生时,Reactor 将事件分发给对应的 Handler 处理器,由 Handler 处理器来具体处理事件。在处理器处理事件的过程中,如果继续需要进行 I/O 操作,它会将 I/O 操作交给 Reactor 处理,由 Reactor 处理器负责监听 I/O 事件并分发给对应的 Handler 处理器。它可以提供高效的事件处理和 I/O 操作,避免了使用传统的同步阻塞式 I/O 模型的性能瓶颈。
Reactor 设计模式 和 Reactive 响应式编程有一定相似之处,两者的核心思想都是事件驱动,通过异步处理来解决高并发下阻塞操作带来的资源消耗和性能下降问题,而 reacotor-netty (GitHub - reactor/reactor-netty: TCP/HTTP/UDP/QUIC client/server with Reactor over Netty)的目标就是将 Netty 框架和响应式组件库 project-reactor (GitHub - reactor/reactor-core: Non-Blocking Reactive Foundation for the JVM)打通.
为了简单起见,我们用HTTP请求和应答处理过程来窥探reactor-netty的实现细节,在 HTTP 协议中,请求和响应消息分为两个部分:消息头和消息体。消息头包含了请求或响应的元数据信息,如请求方法、响应状态码、内容类型、内容长度等。而消息体则包含了请求或响应的实际内容。而当一个 HTTP 请求或响应结束时,需要发送一个空的消息体(EmptyContent)表示请求或响应结束了。因为 HTTP 协议是基于流的,在传输过程中,HTTP 消息是分多次发送的,每次发送都是一部分数据。当发送完最后一部分数据后,需要告诉接收方,请求或响应已经结束了。这个信号就是一个空的消息体。
注意,为了展示方便和理解更容易,文中展示的源代码都经过了精简,请以GitHub上最新代码为准!!!
有了这些基础,我们看一个 reactor-netty 的官方例子,官方给了一个HTTP的client和server的例子,我们这里只分析client端的例子:
String reqStr = "Go to Zibo for barbecue";
System.out.println(Thread.currentThread().getName() + " 开始请求 " + reqStr);
HttpClient httpClient = HttpClient.create().port(8888);
httpClient.post() // Specifies that POST method will be used
.uri("/test/world") // Specifies the path
.send(ByteBufFlux.fromString(Flux.just(reqStr))) // Sends the request body
.responseContent() // Receives the response body
.aggregate()
.asString()
.subscribe(res ->
System.out.println(Thread.currentThread().getName() + " 收到应答 " + res));
从这个client端例子来看,初步看起来和Reactive有关的有两个地方,第一个是send方法入参貌似是一个Flux,另一个是订阅方法subscribe,其中我们用Lambda表达式直接把HTTP的应答结果打印出来,需要注意的是,我们特意也打印了线程的名称,这是为了突出展示Reactive的异步事件驱动特性,即发起请求的线程和接收应答的线程不是同一个线程。这里我们先剖析下client端的实现原理。client端有两个阶段会有IO阻塞,一个是请求发送,一个等待请求应答,请求发送很明显在上述的 send 方法中来做,请求的应答目前看起来像是在responseContent 方法中
深入解析Netty的异步事件驱动与Reactor设计模式

文章详细介绍了Netty的高性能线程模型,主要由Boss线程池和Worker线程池组成,以及Reactor设计模式在Netty中的应用。Reactor负责事件监听和分发,Handler处理具体事件。此外,文章讨论了Reactor设计模式与响应式编程的相似性,并通过HTTP请求处理的例子展示了reactor-netty如何结合Netty和project-reactor。文章还分析了reactor-netty客户端发送请求和接收响应的异步过程,强调了异步事件驱动的重要性,以及在连接管理和资源限制方面的潜在问题。
最低0.47元/天 解锁文章
1718

被折叠的 条评论
为什么被折叠?



