Netty
Netty 基于NIO(NIO 是一种同步非阻塞的I/O 模型,在Java 1.4 中引入了
NIO )。使用 Netty 可以极大地简化并简化了TCP 和UDP 套接字服务器等网
络编程,并且性能以及安全性等很多方面都非常优秀
Netty简介
- Netty 是一个 基于 NIO 的 client-server(客户端服务器)框架,使用
它可以快速简单地开发网络应用程序 - 它极大地简化并优化了TCP 和UDP 套接字服务器等网络编程,并且性能以
及安全性等很多方面甚至都要更好 - 支持多种协议 如FTP,SMTP,HTTP以及各种二进制和基于文本的传统协议
为什么不直接用NIO
- NIO的编程模型复杂而且存在一些BUG,并且对编程功底要求比较高
- NIO在面对断连重连、包丢失、粘包等问题时处理过程非常复杂
Netty的优点
- 统一的 API,支持多种传输类型,阻塞和非阻塞的。
- 简单而强大的线程模型。
- 自带编解码器解决TCP 粘包/拆包问题。
- 自带各种协议栈。
- 真正的无连接数据包套接字支持。
- 比直接使用 Java 核心 API 有更高的吞吐量、更低的延迟、更低的资源消耗
和更少的内存复制 - 安全性不错,有完整的 SSL/TLS 以及 StartTLS 支持
- Dubbo、RocketMQ、Elasticsearch、gRPC、Spark、Elasticsearch 等等热
门开源项目都用到了Netty
Netty和Tomcat的区别
Netty和Tomcat最大的区别就在于通信协议
- Tomcat是基于Http协议的,他的实质是一个基于http协议的web容器
- Netty能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解
码字节流
Netty为什么传输快
零拷贝。一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲
区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会
造成不必要的资源浪费。在堆内存之外开辟一块内存,数据就直接从IO读到了那
块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从
而加快了传输速度。
Netty的应用场景
- 作为RPC 框架的网络通信工具:我们在分布式系统中,不同服务节点之间经常
需要相互调用,这个时候就需要 RPC 框架了。不同服务节点的通信是使用Netty
来做 - 实现一个自己的HTTP 服务器:通过Netty 我们可以自己实现一个简单的HTTP
服务器 - 实现一个即时通讯系统: 使用Netty 我们可以实现一个可以聊天类似微信的
即时通讯系统 - 实现消息推送系统 :市面上有很多消息推送系统都是基于 Netty 来做的
Netty核心组件
Bootstrap 和ServerBootstrap
一种是用于客户端的Bootstrap,一种是用于服务端的ServerBootstrap。
前者的功能是连接到远程主机和端口,后者的功能是绑定本机端口。
Bootstrap 是客户端的启动引导类/辅助类
1 | EventLoopGroup group = new NioEventLoopGroup(); |
ServerBootstrap 服务端的启动引导类/辅助类
1 | // 1.bossGroup 用于接收连接,workerGroup 用于具体的处理 |
- Bootstrap 通常使用connet() 方法连接到远程的主机和端口,作为一
个Netty TCP 协议通信中的客户端。另外Bootstrap 也可以通过 bind()
方法绑定本地的一个端口,作为UDP 协议通信中的一端 - ServerBootstrap通常使用 bind() 方法绑定本地的端口上,然后等待
客户端的连接 - Bootstrap 只需要配置一个线程组—EventLoopGroup,而ServerBootstrap
需要配置两个线程组— EventLoopGroup ,一个用于接收连接,一个用于具体
的 IO 处理
Channel
网络操作抽象类。一旦客户端成功连接服务端,就会新建一个Channel 同该用
户端进行绑定
1 | // 通过 Bootstrap 的 connect 方法连接到服务端 |
比较常用的Channel接口实现类是 :
- NioServerSocketChannel(服务端)
- NioSocketChannel(客户端)
EventLoop
事件循环。EventLoop 的主要作用实际就是责监听网络事件并调用事件处理器进
行相关I/O 操作(读写)的处理。Channel为Netty 网络操作(读写等操作)抽
象类,EventLoop 负责处理注册到其上的Channel 的I/O 操作,两者配合进行
I/O 操作。
EventLoopGroup 包含多个 EventLoop(每一个 EventLoop 通常内部包含一
个线程),它管理着所有的EventLoop的生命周期。并且EventLoop 处理的I/O
事件都将在它专有的Thread 上被处理,即Thread 和EventLoop 属于1:1的关
系,从而保证线程安全。
EventLoopGroup
Netty 中EventLoopGroup 是Reactor 模型的一个实现。
Reactor 就是一个执行while (true) { selector.select();…}循环的线程
,会源源不断产生新的事件,称作反应堆很贴切。 事件又分为连接事件、IO 读和
IO 写事件,一般把连接事件单独放一线程里处理,即主Reactor(MainReactor)
,IO读和IO写事件放到另外的一组线程里处理,即从Reactor(SubReactor),从
Reactor线程数量一般为2*(CPUs - 1)。 所以在运行时,MainReactor只处理
Accept事件,连接到来,马上按照策略转发给从Reactor之一,只处理连接,故
开销非常小;每个SubReactor管理多个连接,负责这些连接的读和写,属于IO
密集型线程,读到完整的消息就丢给业务线程池处理业务,处理完比后,响应
消息一般放到队列里,SubReactor会去处理队列,然后将消息写回
Bytebuf
字节容器,网络通信最终都是通过字节流进行传输的。ByteBuf就是Netty提供
的一个字节容器,其内部是一个字节数组。可以将ByteBuf看作是Netty对Java
NIO 提供了ByteBuffer 字节容器的封装和抽象
ChannelHandler
消息处理器,主要负责处理客户端/服务端接收和发送的数据。
当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。 一
个Channel包含一个ChannelPipeline。 ChannelPipeline为ChannelHandler
的链,一个pipeline 上可以有多个 ChannelHandler。
我们可以在ChannelPipeline 上通过addLast() 方法添加一个或者多个
ChannelHandler(一个数据或者事件可能会被多个Handler 处理)。当
一个ChannelHandler处理完之后就将数据交给下一个ChannelHandler。
当ChannelHandler 被添加到的 ChannelPipeline 它得到一个 ChannelHandlerContext,它代表一个 ChannelHandler 和 ChannelPipeline
之间的“绑定”。 ChannelPipeline 通过 ChannelHandlerContext来间接管理
ChannelHandler 。
ChannelFuture
Netty的核心组件
- Bootstrap 和 ServerBootstrap(启动引导类)Bootstrap 是客户端的
启动引导类/辅助类,ServerBootstrap 客户端的启动引导类/辅助类
- bootstrap 通常使用 connet() 方法连接到远程的主机和端口,作为一个
Netty TCP 协议通信中的客户端。另外,Bootstrap 也可以通过bind() 方
法绑定本地的一个端口,作为UDP 协议通信中的一端 - ServerBootstrap通常使用 bind() 方法绑定本地的端口上,然后等待客户
端的连接 - Bootstrap 只需要配置一个线程组EventLoopGroup,而ServerBootstrap
需要配置两个线程组EventLoopGroup ,一个用于接收连接,一个用于具体的
IO 处理
Channel 和EventLoop 的关系
Channel 为 Netty 网络操作(读写等操作)抽象类,EventLoop 负责处理注册
到其上的Channel 的 I/O 操作,两者配合进行 I/O 操作
EventloopGroup 和EventLoop 的关系
EventLoopGroup 包含多个 EventLoop(每一个 EventLoop 通常内部包含
一个线程),它管理着所有的 EventLoop 的生命周期。EventLoop 处理的
I/O 事件都将在它专有的Thread 上被处理,即Thread 和EventLoop属于
1 : 1 的关系,从而保证线程安全