在学习Netty之前,有必要了解下我们为什么要学习Netty。

什么是Netty

Netty User guide for 4.x中这样介绍

Netty是一个提供异步事件驱动的网络应用框架,是一个用以快速开发高性能、可扩展协议的服务器和客户端的工具。
换句话说,Netty 是一个 NIO 客户端服务器框架,使用它可以快速简单地开发网络应用程序,比如服务器和客户端的协议。Netty 大大简化了网络编程,比如 TCP 和 UDP 的 socket 服务的开发。

为什么要使用Netty

开发网络应用只能用Netty吗?网络应用框架只有Netty吗?如果不是,为什么我要使用它呢?

不是所有的Java网络编程都要使用Netty。

Java的通信模型有同步阻塞I/O模型(BIO)伪异步I/O模型非阻塞I/O(NIO)异步I/O(AIO)四种方式。BIO和伪异步I/O模型对应Java中的java.io。NIO和AIO对应Java中的java.nio。而Netty则是一种NIO框架,同为NIO框架的还有Mina、Grizzly。接下来将逐一介绍这几种通信模型和它们的缺点,并解释为什么要选择Netty而不是原生NIO或者其他NIO框架来进行网络编程。

BIO

在JDK1.4之前,基于Java的所有Socket通信都采用了同步阻塞模型(BIO)

网络编程的基本模型是C/S模型,即两个进程间的通信。服务端提供IP和监听端口,客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可以通过网络套接字(Socket)进行通信。在基于BIO开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。

通过下图描述一下BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理没处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答通信模型。
Netty札记-为什么要学习Netty
该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,Java中的线程也是比较宝贵的系统资源,线程数量快速膨胀后,系统的性能将急剧下降,随着访问量的继续增大,系统会发生线程堆栈溢出等问题,最终导致系统不能对外提供服务。
这种“请求-通信”模型简化了上层应用的开发,但在性能和可靠性方面存在着巨大的瓶颈。当并发访问量逐渐增大,采用BIO的服务端应用只有通过硬件的不断扩容来满足高并发。随着集群规模的不断膨胀,可维护性也面临巨大的挑战,只能通过采购性能更高的服务器来解决问题,这回导致恶性循环。

伪异步I/O

为了改进BIO中的问题,后来出现了一种使用线程池或消息队列实现1个或多个线程处理N个客户端的模型。但是底层还是使用的同步阻塞I/O,通常被称为伪异步I/O模型。

为了解决BIO中一个请求需要一个线程来处理的问题,对BIO线程模型做了优化,通过一个线程池来处理多个客户端的请求。通过线程池可以灵活地配置线程资源,设置线程池的最大值,防止由于海量并发导致线程耗尽。

伪异步I/O的模型图如图所示,当新的客户端接入是,将客户端的Socket封装成Task,让线程池来处理。
Netty札记-为什么要学习Netty
由于线程池采用阻塞队列实现,当队列积满后,后续入队列的操作将被阻塞。由于只有一个acceptor线程接收客户端输入,它被阻塞在线程池的同步阻塞队列后,新的客户端请求将被拒绝,客户端将发生大量的连接超时。由于大量的连接超时客户端会认为系统已经崩溃,无法提供服务。伪异步I/O仅仅是对同步阻塞IO的简单优化,无法从根本上解决同步阻塞I/O导致的通信线程阻塞问题。

NIO

JDK1.4推出了支持了非阻塞I/O的NIO类库。NIO极大地促进了基于Java的异步非阻塞编程的发展。NIO提供了与传统BIO模型中的Socket和ServerSocket相对应的SocketChannel和ServerSocketChannel两种不同的套接字通道实现。新增的着两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用NIO的非阻塞模式来开发。

但它依然有不完善的地方,特别是对文件系统的处理不足。

AIO

JDK1.7将NIO升级为NIO2.0,完善了对文件系统的处理,正式提供了异步文件I/O的操作,同时提供了与UNIX网络编程事件驱动I/O对应的AIO。

NIO 2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。异步的套接字通道时真正的异步非阻塞I/O,对应于UNIX网络编程中的事件驱动I/O(AIO)。它不需要过多的Selector对注册的通道进行轮询即可实现异步读写,从而简化了NIO的编程模型。

4种I/O模型的对比

对比项/I/O模型 同步阻塞I/O(BIO) 伪异步I/O 非阻塞I/O(NIO) 异步I/O(AIO)
客户端个数:I/O线程 1:1 M:N(其中M可以大于N) M:1(1个I/O线程处理多个客户端连接) M:0(不需要启动额外的I/O线程,被动回调)
I/O类型(阻塞) 阻塞I/O 阻塞I/O 非阻塞I/O 非阻塞I/O
I/O类型(同步) 同步I/O 同步I/O 同步I/O(I/O多路复用) 异步I/O
API使用难度 简单 简单 复杂 复杂
调试难度 简单 简单 复杂 复杂
可靠性 非常差
吞吐量

并不是所有的Java网络编程都要选择NIO和Netty。具体选择什么样的模型或者NIO框架,完全基于业务的实际应用场景和性能需求,如果客户端很少,服务器负荷不重,就没有必要选择开发起来相对不那么简单的NIO做服务端;相反,就应考虑使用NIO或者相关的框架了。

为什么选择Netty

为什么不使用原生NIO
  • NIO的类库和API繁杂。需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
  • 需要很多额外的技能做铺垫。例如需要熟悉Java多线程编程。
  • Java NIO存在很严重的BUG,比如臭名昭著的epoll bug,导致Selector空轮询:这个bug会导致linux上导致cpu 100%。
Netty优点
  • API使用简单,开发门槛低。
  • 功能强大,预置了多种编解码功能,支持多种协议开发。
  • 定制能力强,可以通过ChannelHadler进行扩展。
  • 性能高,对比其它NIO框架,Netty综合性能最优。
  • 稳定,修复了NIO出现的所有Bug。
  • 经历了大规模的应用验证。在互联网、大数据、网络游戏、企业应用、电信软件得到成功。
Netty与Mina

待补充

Netty与Grizzly

待补充

参考:《Netty权威指南》

相关文章:

  • 2021-10-29
  • 2021-01-11
  • 2022-12-23
  • 2021-05-17
  • 2022-12-23
  • 2021-10-07
  • 2021-11-30
  • 2021-10-13
猜你喜欢
  • 2021-07-29
  • 2022-02-08
  • 2021-11-02
  • 2022-01-17
  • 2021-10-28
  • 2022-12-23
  • 2021-05-14
相关资源
相似解决方案