Netty是什么?

用官方的描述:

Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。它极大地简化了网络编程。

Netty是经过精心设计的,它借鉴了许多协议(如 FTP、SMTP、HTTP 以及各种基于二进制和基于文本的遗留协议)的实现经验。因此,Netty 成功地找到了一种方法,可以在不妥协的情况下实现易于开发、性能较高、并兼具了稳定性和灵活性。

如果你没有接触过netty,对上面描述还是感觉略抽象的话,那么,

用一句话总结就是:

Netty 在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用

Netty早已被广泛应用于我们看不见的地方。

我们日常用到的一些中间件如:RocketMQ、ElasticSearch、Dubbo、gRPC、Zookeeper等,你若是详细研究,看看其源码,就会发现在其底层实现上都用了Netty。

初识Netty

Netty的优点,概括一下就是:

1)使用简单;

2)功能强大;

3)性能强悍。

Netty的特点:

1)高并发:基于 NIO(Nonblocking IO,非阻塞IO)开发,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高;

2)传输快:传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输;

3)封装好:封装了 NIO 操作的很多细节,提供了易于使用调用接口。

Netty是一个高性能、异步事件驱动的NIO框架,提供了对TCP、UDP和文件传输的支持,核心功能是让客户端和服务端两者之间进行通信交流。

如果上述这种说法太过于官方,不容易理解的话,那我换种无脑式说法。Netty的作用是,对TCP/UDP编程进行了简化和封装,提供了更容易使用的网络编程接口。

所以,说到这里,我们就明白为什么Dubbo、gRPC等RPC框架会用到Netty了吧,因为它们都存在服务消费者调用服务提供者的场景。

可以这样说,但凡用Java开发的、跟网络IO相关的中间件,基本上少不了Netty的影子。

另外,Netty的底层是依赖于JDK中的NIO,在此之上进行了优化,包括如下几点:

  • 简化概念,降低编程复杂性。

  • 在性能上得到很大提升。

  • 解决了JDK中epoll selector空轮询导致CPU 100%的问题。

Netty & Tomcat

Netty 和 Tomcat 最大的区别在于对通信协议的支持。Tomcat 是基于 HTTP 协议的,本质是一个基于HTTP协议的Web容器,而Netty则是以TCP、UDP协议为主,并支持通过编程自定义各种协议。其它的不同点诸如:

  • 底层网络通信模型不同:Tomcat 是基于阻塞的 BIO(Blocking I/O)模型实现的,而 Netty 是基于 NIO(Non-Blocking I/O)模型实现的。

  • 线程模型不同:Tomcat 使用传统的多线程模型,每个请求都会分配一个线程,而 Netty 使用 EventLoop 线程模型,每个 EventLoop 负责处理多个连接,通过线程池管理 EventLoop。

  • 协议支持不同:Tomcat 内置支持 HTTP 和 HTTPS 协议,而 Netty 不仅支持 HTTP 和 HTTPS 协议,还支持 TCP、UDP 和 WebSocket 等多种协议。

  • 代码复杂度不同:由于Tomcat支持的功能比较全面,所以其代码相对较为复杂,而 Netty 的代码相对比较简洁、精简。

  • 应用场景不同:Tomcat 适合于处理比较传统的 Web 应用程序,如传统的 MVC 模式Web应用程序;而 Netty 更适合于高性能、低延迟的网络应用程序,如游戏服务器、即时通讯服务器等。 

Netty的核心组件

从某种意义上说,了解了Netty的核心组件,也就理顺的它整体的运行过程。

网上基于这五个组件的解释众说纷纭,我不得已在此基础上加入了自己的理解。

Channel:相当于socket,与另一端进行通信的通道,具备bind、connect、read、write等IO操作的能力。

EventLoop:事件循环,负责处理Channel的IO事件,一个EventLoopGroup包含多个EventLoop,一个EventLoop可被分配至多个Channel,一个Channel只能注册于一个EventLoop,一个EventLoop只能与一个Thread绑定。

ChannelFuture:channel IO事件的异步操作结果。 

ChannelHandler:包含IO事件具体的业务逻辑。

ChannelPipeline:ChannelHandler的管道容器。

Netty的高性能

从宏观来讲,Netty的高性能主要在于:Reactor模式、ZeroCopy(零拷贝)和对象池。

 (1)Reactor模式

通过设置不同的启动参数,Netty可以同时支持单Reactor单线程模型、单Reactor多线程模型和主从Reactor多线层模型。

上述3种的线程模型具体为:

单Reactor单线程示意图

图片

单Reactor多线程

一个线程负责监听服务端,接受客户端TCP连接请求;一个线程同时处理多条链路,一个链路只对应一个线程

主从Reactor多线程示意图

图片

服务器NettyReactor的工作架构

每个端口对应一个boss线程

图片

Reactor工作原理:

①Reactor主线程 MainReactor 对象通过select 监听连接事件, 收到事件后,通过Acceptor 处理连接事件

②当 Acceptor 处理连接事件后,MainReactor 将连接分配给SubReactor

③subReactor 将连接加入到连接队列进行监听,并创建handler进行各种事件处理

④当有新事件发生时, subreactor 就会调用对应的handler处理

⑤handler 通过read 读取数据,分发给后面的worker 线程处理

⑥worker 线程池分配独立的worker 线程进行业务处理,并返回结果

⑦handler 收到响应的结果后,再通过send 将结果返回给client

⑧Reactor 主线程可以对应多个Reactor 子线程, 即MainRecator 可以关联多个SubReactor

Reactor模型思想:

分而治之 + 事件驱动(优点:模块化、高性能—把大拆小,减少阻塞时间)

分而治之

一个连接里完整的网络处理过程一般分为accept、read、decode、process、encode、send(write)这几步。

Reactor模式将每个步骤映射为一个Task,服务端线程执行的最小逻辑单元不再是一次完整的网络请求,而是Task,且采用非阻塞方式执行。

事件驱动

相应的Task(accept、read、write)对应特定网络事件。当Task准备就绪时,Reactor收到对应的网络事件通知,并将Task分发给绑定了对应网络事件的Acceptor和Handler执行。

Netty基于Reactor模型实现,Reactor模型主要由Acceptor、Reactor、Handler组成。

  • Reactor:事件分派器,将I/O事件分派给对应的Handler和Acceptor。

  • Acceptor:多路复用器,处理客户端新连接。

  • Handler:事件处理器,处理IO读写任务。

Netty 通过 Reactor 模型基于多路复用器接收并处理用户请求,内部实现了两个线程池, boss 线程池和 worker 线程池,其中 boss 线程池的线程负责处理请求的 accept 事件,当接收 到 accept 事件的请求时,把对应的 socket 封装到一个 NioSocketChannel 中,并交给 worker 线程池,其中 work 线程池负责请求的 read 和 write 事件,由对应的 Handler 处理。

Netty线程模型:

图片

(2)ZeroCopy(零拷贝) 

零拷贝技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域,这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。

举例来说,如果要读取一个文件并通过网络发送它,传统方式下每个读/写周期都需要复制两次数据和切换两次上下文,而数据的复制都需要依靠CPU。通过零复制技术完成相同的操作,上下文切换减少到两次,并且不需要CPU复制数据。

Netty中的零拷贝

  • 使用CompositeByteBuf 类可以将多个ByteBuf合并为一个逻辑上的ByteBuf,避免了各个ByteBuf之间的拷贝。

  • ByteBuf 支持 slice 操作,因此可以将ByteBuf分解为多个共享同一个存储区域的 ByteBuf,避免了内存的拷贝。

  • 通过FileChannel.tranferTo 实现文件传输,可直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环 write方式导致的内存拷贝问题。

(3)对象池

对象池模式(The Object Pool Pattern)是单例模式的一个变种,对象池模式管理一个可代替对象的集合,组件从池中借出对象,用它来完成一些任务并当任务完成时归还该对象。

Netty中的Recycler,该类是个容器,基于ThreadLocal实现的的轻量级对象池,内部主要是一个Stack结构。当需要使用一个实例时,就弹出,当使用完毕时,就清空后入栈。

拆包粘包

TCP是一种流协议(stream protocol),先把数据流拆分成适当长度的报文段,然后TCP把数据包传给IP层,由它来通过网络将数据包传送给接收端的IP层。

MTU (Maxitum Transmission Unit),最大传输单元,是链路层对一次可以发送的最大数据的限制。

MSS(Maxitum Segment Size),最大分段大小,是TCP报文中data部分的最大长度,是传输层对一次可以发送的最大数据的限制。

MTU(1500字节) =  MSS(1460字节) +  TCP header (20字节) +  IP header (20字节)

假设客户端分别发送两个数据包D1、D2个服务端:

  • 粘包:服务端一次接收到了D1和D2两个数据包,两个包粘在一起;

  • 拆包:服务端分三次读到了数据部分,第一次读到了D1包,第二次读到了D2包的部分内容,第三次读到了D2包的剩下内容;

  • 拆包粘包:服务端分两次读到了数据包,第一次读到了D1和D2的部分内容,第二次读到了D2的剩下部分;

解决方案

  • 设置定长消息,服务端每次读取既定长度的内容作为一条完整消息(如:不足补0000等)。

  • 设置消息边界,服务端从网络流中按消息编辑分离出消息内容(如:数据边界的标识为换行符"\n")。

  • 使用带消息头的协议,消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。

Netty中的解决方案

  • FixedLengthFrameDecoder(使用定长的报文来分包)

  • DelimiterBasedFrameDecoder(添加特殊分隔符报文来分包)

  • LineBasedFrameDecoder(数据未尾添加回车换行符来分包)

  • LengthFieldBasedFrameDecoder(使用消息头和消息体来分包)

这里只列举常见的。,更长全面的内容,欢迎持续关注,后面会推出相关文章。

Netty长连接、心跳机制

在网络编程中,长连接是指客户端与服务器之间建立的连接可以保持一段时间,以便在需要时可以快速地进行数据交换。与短连接相比,长连接可以避免频繁建立和关闭连接的开销,从而提高数据传输的效率和性能。

Netty 提供了一种长连接的实现方式,即通过 Channel 的 keepalive 选项来保持连接的状态。当启用了 keepalive 选项后,客户端和服务器之间的连接将会自动保持一段时间,如果在这段时间内没有数据交换,客户端和服务器之间的连接将会被关闭。通过这种方式,可以实现长连接,避免频繁建立和关闭连接的开销。

除了 keepalive 选项之外,Netty 还提供了一种心跳机制来保持连接的状态。心跳机制可以通过定期向对方发送心跳消息,来检测连接是否正常。如果在一段时间内没有收到心跳消息,就认为连接已经断开,并进行重新连接。Netty 提供了一个 IdleStateHandler 类,可以用来实现心跳机制。IdleStateHandler 可以设置多个超时时间,当连接空闲时间超过设定的时间时,会触发一个事件,可以在事件处理方法中进行相应的处理,比如发送心跳消息。

通过使用长连接和心跳机制,可以保证客户端与服务器之间的连接处于正常的状态,从而提高数据传输的效率和性能。特别是在处理大量数据传输的场景中,长连接和心跳机制可以降低建立和关闭连接的开销,减少网络负载,提高系统的稳定性。 

Netty的IO模型

Netty的IO模型是基于事件驱动的NIO(Non-blocking IO)模型。在传统的BIO(Blocking IO)模型中,每个连接都需要一个独立的线程来处理读写事件,当连接数过多时,线程数量就会爆炸式增长,导致系统性能急剧下降。而在NIO模型中,一个线程可以同时处理多个连接的读写事件,大大降低了线程的数量和切换开销,提高了系统的并发性能和吞吐量。

与传统的NIO模型相比,Netty的NIO模型有以下不同点:

  • Netty使用了Reactor模式,将IO事件分发给对应的Handler处理,使得应用程序可以更方便地处理网络事件。

  • Netty使用了多线程模型,将Handler的处理逻辑和IO线程分离,避免了IO线程被阻塞的情况。

  • Netty支持多种Channel类型,可以根据应用场景选择不同的Channel类型,如NIO、EPoll、OIO等。 

Netty处理大文件的传输

搭建文件传输也是网络必须要处理的内容之一,而且,netty对这种也有比较特别的设计。

在Netty中,可以通过使用ChunkedWriteHandler处理大文件的传输。ChunkedWriteHandler是一个编码器,可以将大文件切分成多个Chunk,并将它们以ChunkedData的形式写入管道,这样就可以避免一次性将整个文件读入内存,降低内存占用。

具体使用方法如下:

  • 在服务端和客户端的ChannelPipeline中添加ChunkedWriteHandler。

  • 在服务端和客户端的业务逻辑处理器中,接收并处理ChunkedData。

  • 在客户端向服务端发送数据时,将需要传输的文件包装成ChunkedFile并写入管道。

在传输大文件时,还需要注意以下几点:

  • 使用ChunkedFile时需要指定Chunk的大小,根据实际情况选择合适的大小,一般建议不要超过8KB。

  • 为了避免大文件传输过程中对网络造成影响,可以在服务端和客户端的ChannelPipeline中添加WriteBufferWaterMark,限制写入缓冲区的大小。 

最后,我再总结下netty的主要特点和优势

  1. 异步和事件驱动:Netty采用异步非阻塞的IO模型,允许处理大量并发连接而不会阻塞应用程序线程。它使用事件驱动的方式来处理网络事件,这使得编写高效的网络应用程序变得更容易。

  2. 高性能:Netty在性能方面表现出色,其底层的NIO实现充分利用了现代操作系统的异步IO特性,能够处理大量并发连接和数据传输,同时保持低延迟。

  3. 可扩展性:Netty提供了灵活的扩展机制,可以轻松地定制和扩展功能,以满足不同应用程序的需求。它支持各种协议和编解码器,如HTTP、WebSocket、TLS/SSL等。

  4. 安全性:Netty内置了对TLS/SSL的支持,可以加密网络连接以确保数据的安全传输。

  5. 多协议支持:Netty支持多种网络协议,包括TCP、UDP、HTTP、WebSocket等,使其适用于各种应用场景。

  6. 大型社区和活跃开发:Netty有一个庞大的开发社区,不断更新和改进框架,以适应新的技术和需求。

以上为全部内容。

  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值