全网讲解最透彻:高性能网络应用框架Netty,仅此一篇

本文深入探讨了BIO线程模型在处理大量连接时的局限性,并引出了非阻塞式I/O(NIO)的重要性。通过Reactor模式,解释了如何用一个线程处理多个连接,进而引入了Netty的线程模型。Netty中的EventLoop负责监听网络事件,以单线程处理每个连接,避免并发问题。文章还提到了Netty中的bossGroup和workerGroup,以及它们在处理连接请求和读写请求中的分工,并展示了基于Netty实现echo服务端的简单代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BIO 这种线程模型适用于 socket 连接不是很多的场景;但是现在的互联网场景,往往需要服务器能够支撑十万甚至百万连接,而创建十万甚至上百万个线程显然并不现实,所以BIO 线程模型无法解决百万连接的问题。如果仔细观察,你会发现互联网场景中,虽然连接多,但是每个连接上的请求并不频繁,所以线程大部分时间都在等待 I/O 就绪。也就是说线程大部分时间都阻塞在那里,这完全是浪费,如果我们能够解决这个问题,那就不需要这么多线程了。

顺着这个思路,我们可以将线程模型优化为下图这个样子,可以用一个线程来处理多个连接,这样线程的利用率就上来了,同时所需的线程数量也跟着降下来了。这个思路很好,可是使用 BIO 相关的 API 是无法实现的,这是为什么呢?因为 BIO 相关的 socket 读写操作都是阻塞式的,而一旦调用了阻塞式 API,在 I/O 就绪前,调用线程会一直阻塞,也就无法处理其他的 socket 连接了。

在这里插入图片描述

好在 Java 里还提供了非阻塞式(NIO)API,利用非阻塞式 API 就能够实现一个线程处理多个连接了。那具体如何实现呢?现在普遍都是采用 Reactor 模式,包括 Netty 的实现。所以,要想理解 Netty 的实现,接下来我们就需要先了解一下 Reactor 模式。

Reactor 模式

下面是 Reactor 模式的类结构图,其中 Handle 指的是 I/O 句柄,在 Java 网络编程里,它本质上就是一个网络连接。Event Handler 很容易理解,就是一个事件处理器,其中handle_event() 方法处理 I/O 事件,也就是每个 Event Handler 处理一个 I/O Handle;get_handle() 方法可以返回这个 I/O 的 Handle。Synchronous Event Demultiplexer 可以理解为操作系统提供的 I/O 多路复用 API,例如 POSIX 标准里的 select() 以及 Linux 里面的 epoll()。

在这里插入图片描述

Reactor 模式的核心自然是Reactor 这个类,其中 register_handler() 和remove_handler() 这两个方法可以注册和删除一个事件处理器;handle_events() 方式是核心,也是 Reactor 模式的发动机,这个方法的核心逻辑如下:首先通过同步事件多路选择器提供的 select() 方法监听网络事件,当有网络事件就绪后,就遍历事件处理器来处理该网络事件。由于网络事件是源源不断的,所以在主程序中启动 Reactor 模式,需要以while(true){} 的方式调用 handle_events() 方法。

void Reactor : : handle_events()

{

/*

  • 通过同步事件多路选择器提供的

  • select() 方法监听网络事件

*/

select( handlers );

/* 处理网络事件 */

for ( h in handlers )

{

h.handle_event();

}

}

/* 在主程序中启动事件循环 */

while ( true )

{

handle_events();

[](()Netty 中的线程模型


Netty 的实现虽然参考了 Reactor 模式,但是并没有完全照搬,Netty 中最核心的概念是事件循环(EventLoop),其实也就是 Reactor 模式中的 Reactor,负责监听网络事件并调用事件处理器进行处理。在 4.x 版本的 Netty 中,网络连接和 EventLoop 是稳定的多对1 关系,而 EventLoop 和 Java 线程是 1 对 1 关系,这里的稳定指的是关系一旦确定就不再发生变化。也就是说一个网络连接只会对应唯一的一个 EventLoop,而一个 EventLoop也只会对应到一个 Java 线程,所以一个网络连接只会对应到一个 Java 线程。

一个网络连接对应到一个 Java 线程上,有什么好处呢?最大的好处就是对于一个网络连接的事件处理是单线程的,这样就避免了各种并发问题。

Netty 中的线程模型可以参考下图,这个图和前面我们提到的理想的 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 线程模型图非常相似,核心目标都是用一个线程处理多个网络连接。

在这里插入图片描述

Netty 中还有一个核心概念是EventLoopGroup,顾名思义,一个 EventLoopGroup 由一组 EventLoop 组成。实际使用中,一般都会创建两个 EventLoopGroup,一个称为bossGroup,一个称为 workerGroup。为什么会有两个 EventLoopGroup 呢?

这个和 socket 处理网络请求的机制有关,socket 处理 TCP 网络连接请求,是在一个独立的 socket 中,每当有一个 TCP 连接成功建立,都会创建一个新的 socket,之后对 TCP 连接的读写都是由新创建处理的 socket 完成的。也就是说处理 TCP 连接请求和读写请求是通过两个不同的 socket 完成的。上面我们在讨论网络请求的时候,为了简化模型,只是讨论了读写请求,而没有讨论连接请求。

在 Netty 中,bossGroup 就用来处理连接请求的,而 workerGroup 是用来处理读写请求的。bossGroup 处理完连接请求后,会将这个连接提交给 workerGroup 来处理,workerGroup 里面有多个 EventLoop,那新的连接会交给哪个 EventLoop 来处理呢?这就需要一个负载均衡算法,Netty 中目前使用的是轮询算法。

下面我们用 Netty 重新实现以下 echo 程序的服务端,近距离感受一下 Netty。

[](()用 Netty 实现 Echo 程序服务端


下面的示例代码基于 Netty 实现了 echo 程序服务端:首先创建了一个事件处理器(等同于 Reactor 模式中的事件处理器),然后创建了 bossGroup 和 workerGroup,再之后创建并初始化了 ServerBootstrap,代码还是很简单的,不过有两个地方需要注意一下。

第一个,如果 NettybossGroup 只监听一个端口,那 bossGroup 只需要 1 个 EventLoop就可以了,多了纯属浪费。

第二个,默认情况下,Netty 会创建“2*CPU 核数”个 EventLoop,由于网络连接与EventLoop 有稳定的关系,所以事件处理器在处理网络事件的时候是不能有阻塞操作的,否则很容易导致请求大面积超时。如果实在无法避免使用阻塞操作,那可以通过线程池来异步处理。

/* 事件处理器 */

final EchoServerHandler serverHandler

= new EchoServerHandler();

/* boss 线程组 */

EventLoopGroup bossGroup

= new NioEventLoopGroup( 1 );

/* worker 线程组 */

EventLoopGroup workerGroup

= new NioEventLoopGroup();

try {

ServerBootstrap b = new ServerBootstrap();

b.group( bossGroup, workerGroup )

.channel( NioServerSocketChannel.class )

.childHandler( new ChannelInitializer()

{

@Override

public void initChannel( SocketChannel ch )

{

ch.pipeline().addLast( serverHandler );

}

} );

极化码(Polar Code)是由土耳其科学家Erdal Arıkan在2009年提出的一种新型纠错编码技术。它通过利用信道的极化现象,将虚拟信道分为误码率接近0和接近1/2的两类。在编码设计中,数据被放置在误码率极低的信道上,从而实现高效的数据传输。极化码的主要优势在于其理论编码容量能够达到香农限,并且构造方法较为简单。 MATLAB是一种功能强大的数学计算和编程工具,广泛应用于科学研究和工程领域。在极化码的研究中,MATLAB可用于构建编码和解码算法,模拟数据在不同信道条件下的传输效果,验证理论性能,并优化相关参数。 SC(Successive Cancellation,逐位取消)译码是极化码的基本解码方法。它从可靠的比特开始,依次解码每个虚拟信道,且每个比特的解码结果会影响后续比特的解码,因为它们之间存在依赖关系。虽然SC译码的实现较为简单,但其计算复杂度较高,随着码长的增加,解码时间会线性增长。 SCL(Successive Cancellation List,逐位取消列表)译码是SC译码的改进版本。它通过引入列表机制,同时处理多个路径,从而增强了错误校正能力,并在一定程度上降低了错误率。与SC译码相比,SCL译码虽然需要消耗更多的计算资源,但能够提供更好的性能。 一个完整的MATLAB仿真资源通常包含以下内容: 编码模块:用于实现极化码的生成,包括码字构造和极化矩阵操作等。 信道模型:用于模拟各种通信信道,例如AWGN(加性高斯白噪声)信道或衰落信道。 SC/SCL译码模块:包含SC译码和SCL译码的算法实现。 误码率(BER)计算:通过比较发送和接收的码字,计算误码率,以评估编码性能。 性能曲线绘制:绘制误码率与信噪比(SNR)之间的关系曲线,展示不同译码策略的性能差异。 使用说明:指导用户如何运行仿真,理解代码结构,以及如何调整参数以进行自定义实验。 代码注
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值