线程模型
您知道此页面是从 Github Wiki 页面 自动生成的,对吧?您可以在 此处 自行改进!
简单来说,对于一个通道
- 无论其传输和类型如何,其所有上游(即入站)事件都必须从为通道执行 I/O 的线程(即 I/O 线程)触发。
- 所有下游(即出站)事件都可以从任何线程(包括 I/O 线程和非 I/O 线程)触发。但是,任何作为下游事件的副作用而触发的上游事件都必须从 I/O 线程触发。(例如,如果
Channel.close()
触发channelDisconnected
、channelUnbound
和channelClosed
,则它们必须由 I/O 线程触发。
当前问题(难看 - 导致上游处理程序中的竞争条件,糟糕 - 不会导致竞争条件但违反了预期的线程模型)
-
难看:作为下游事件的副作用而触发的上游事件由调用线程触发,
-
难看:本地传输始终使用调用线程触发事件。
-
糟糕:
channelOpen
由调用ChannelFactory.newChannel()
的线程触发,该线程不是 I/O 线程。这有点糟糕,但否则无法通过在此处关闭通道来限制并发活动通道。如果我们在 IO 线程中执行此操作,则效率不会那么高。 -
糟糕:客户端通道由两个 I/O 线程运行。一个进行连接尝试,另一个执行实际 I/O。
操作项
- 将客户端 boss、服务器端 boss 和 NioWorker 合并到一个通用的 I/O 线程中,该线程可以执行所有 I/O 操作。通过这样做
- 我们解决了客户端通道问题,因为尝试连接的线程可以继续执行读取和写入。
- 我们解决了 Netty 创建与打开的服务器端口数一样多的线程的问题。
- 我们可以更轻松地共享 NioWorkers 池,并且在通道-工作程序映射中可能具有更大的灵活性。
- 我们还需要调查是否可以创建一个抽象 I/O 线程类,以便所有传输(套接字、数据报、SCTP 等)都可以对其进行扩展。我们目前在套接字、数据报和 SCTP 之间存在太多重复。
- 如果调用线程不是 I/O 线程,则 Netty 会稍后在 I/O 线程中触发上游事件。随着这一更改,允许用户稍后在 I/O 线程中触发自己的上游事件,方法是将
sendUpstreamLater()
方法添加到ChannelPipeline
和ChannelHandlerContext
。- 但是,我们不能仅在当前线程不是 I/O 线程时使用
sendUpstreamLater()
,因为OMATPE
或MATPE
会干扰它,因此我们必须让用户决定。(即调用sendUpstream()
或sendUpstreamLater()
)
- 但是,我们不能仅在当前线程不是 I/O 线程时使用
-
ChannelFactory.newChannel()
不得立即触发事件。newChannel()
必须等到 I/O 线程通知通道已注册到 I/O 线程后,才能将新通道返回给调用方。 - 重写本地传输。
问题
- 我们可以在 v3 中进行所有这些更改,同时保持向后兼容性吗?在 v4 中完成这些更改岂不是更容易?完全异步的用户应用程序在处理程序中执行所有 I/O,大量使用
ChannelFuture
,不应该受到当前有缺陷的线程模型的影响,这意味着用户可以想办法解决此问题,因此最好继续使用 v4,而不是在两个分支上进行相同的更改。
回答
- 我认为如果将它“反向移植”到 v3 中的工作量太大,我们应该继续前进并“忽略”v3。也许我们可以找到一些针对 v3 的“更简单的”解决方法,这至少可以帮助我们摆脱 Channel.close() 竞争,因为这很可能会影响我们的用户。(normanmaurer)
上次检索时间为 2024 年 7 月 19 日