• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

浅出讲Netty

武飞扬头像
库森学长
帮助1

很多初中级工程师,他们心中的一大疑惑点就是:后端工程师除了在日常工作中,写写CRUD,敲几个if else,打几行日志,用用Redis缓存之外,还能学些什么知识来提升自己?

于是,有些同学就把目标喵向了我们日常用到的一些中间件,开始研究它们的实现原理或底层源码,如:Redis、Kafka、RocketMQ、RabbitMQ、ElasticSearch、Dubbo、Thrift、gRPC、Zookeeper、Nginx等等。

往往一研究,他们才发现,这里面有相当一部分中间件,如:RocketMQ、ElasticSearch、Dubbo、gRPC、Zookeeper等,在底层实现上都用了Netty。

那么,Netty到底是何方神圣呢?

初识Netty

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协议为主,并支持通过编程自定义各种协议。

核心组件

从某种意义上说,了解的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的高性能主要在于:Reactor模式、Zero Copy和对象池。

(1)Reactor模式

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

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)Zero Copy

Zero Copy技术是指计算机执行操作时,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(使用消息头和消息体来分包)

Btw:这里只列举常见的。

结语

其实,Netty最经典之处在于它的源码,包括服务启动、NioEventLoop、ChannelPineline、accept操作、read操作、write操作,以及内存分配管理。

强烈建议想提升自己的Javaer好好看看。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgeefhh
系列文章
更多 icon
同类精品
更多 icon
继续加载