性能文章>搭稳Netty开发的地基,用漫画帮你分清同步异步阻塞非阻塞>

搭稳Netty开发的地基,用漫画帮你分清同步异步阻塞非阻塞转载

8月前
232577

Netty
Netty是一款非常优秀的网络编程框架,是对NIO的二次封装,本文将重点剖析Netty客户端的启动流程,深入底层了解如何使用NIO编程客户端。

Linux网络编程5种IO模型
根据UNIX网络编程对于IO模型的分类,UNIX提供了5种IO模型,分别是 阻塞IO 、 非阻塞IO、 IO复用 、 信号驱动IO 、 异步IO 。这几种IO模型在《UNIX网络编程》中有详解,这里作者只简单介绍,帮助大家回忆一下这几种模型。

对于Linux来说,所有的操作都是基于文件的,也就是我们非常熟悉的fd,在缺省的情况下,基于文件的操作都是 阻塞的 。下面就通过系统调用 recvfrom 来回顾下这五种模型。模型的图示来源于《Netty权威指南》下面不再说明。

系统调用 recvfrom 直到有数据到达并且从内核空间copy到用户空间才返回,这期间 recvfrom调用者一直等待,这就是阻塞IO。

image.png
非阻塞IO
应用进程反复调用 recvfrom 询问操作系统内核数据是否准备好,相对于阻塞来说,你可以理解为非阻塞自主能力变强。从图中可以看出数据没有准备好的时候内核返回 EWOULDBLOCK 。
image.png

Linux给我们提供的IO复用相关的函数有 select 、 poll 、 epoll 这几个函数的优缺点这里就不做详述了,想要再深入了解的同学可以看下我以前的这篇文章: epoll详解:从底层了解IO复用 。

image.png
信号驱动IO
应用进程建立一个 SIGIO信号 处理程序,当内核数据准备好的时候,会产生一个 SIGIO信号 ,处理程序收到这个信号并通知进程调用 recvfrom 。

image.png
应用程序执行系统调用,告知内核某个操作,并让内核在整个操作完成后(包括将数据从内核复制到应用缓冲区)通知应用进程。这种模式与信号驱动模式的主要区别在于:信号驱动IO由内核通知我们何时可以开始IO操作。异步驱动IO由内核通知我们IO操作是否完成。
image.png

用漫画帮你分清同步异步阻塞非阻塞
说到 同步 、 异步 、 阻塞 、 非阻塞 很多人分不清他们之间的区别,每次听到这几个词语的时候就头大。更别说他们的组合 同步阻塞 、 同步非阻塞 、 异步阻塞 、 异步非阻塞 了。针对这几个大家非常容易混淆的词语,我画了一幅漫画帮助大家理解。欢迎大家来吐槽我的第一个漫画作品《洗衣服的故事》。
image.png
image.png
image.png
image.png

你看到这四个词语的时候肯定以为是说的同一件事,描述的是同一个对象,这种理解是错误的。我们可以把 同步、异步 看做一组, 阻塞和非阻塞 看成一组。这两者的主要区别就是:前者是" 消息通知机制 ",后者是“ 等到消息通知时的状态 ”。

这样说你可能还是没有理解,结合上面的漫画情景中"消息通知机制"就是洗衣机,“等待消息通知时的状态"就是人。拿"异步非阻塞"举例,洗衣机洗完衣服会发出"滴滴"声通知人已经完成了"洗衣服"这个任务,这就是"消息通知机制”,这个过程中人不必傻等着洗衣机"洗衣服"这个任务的结果,可以干其他的事情“浇花”,听到洗衣机的"滴滴"声之后可以去"晾衣服"。这就是一种"等待消息通知时的状态"。

从BIO到AIO
BIO通信模型

BIO通信模型也叫 一请求一应答 模型。顾名思义,服务器每接收到一个请求就会生成一个线程来处理这个请求。
image.png

这种模式最大的问题是缺乏弹性伸缩能力。这种模式接收一个请求就开启一个线程去处理。众所周知,线程资源对于JVM虚拟机是极其宝贵的,线程数过度膨胀的时候,系统性能会急剧下降,并发很大的情况下很容易会导致资源耗尽而出现堆栈溢出,进而出现宕机或者僵死、服务器崩溃,无法对外服务。上面图示中Client5就是资源耗尽时服务器无法提供服务的场景,用户看到的将是异常或者超时。

为异步IO模型

用过线程池的同学都知道线程池的好处是我们可以指定 核心线程数 、 最大线程数 、 线程池队列大小 并且可以设置 拒绝策略 。所以这种模式相比于阻塞IO来说线程的创建,服务的范围是 可控的 。无论多少个客户端并发,都不会导致服务器资源的耗尽和宕机。
image.png

NIO编程

NIO是 Non-block I/O(非阻塞IO) 的简称。使用IO复用编写的服务器和客户端就是NIO编程的很好的示例。我们设置了读写都是非阻塞之后,当没有可读或者可写的数据的时候,线程不同步等待,会直接返回。想要再深入了解的同学可以看下我以前的这篇文章: epoll详解:从底层了解IO复用 。

AIO编程

NIO 2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。

异步通道获取获取操作结果方式:

1.使用 java.util.concurrent.Future类 表示异步操作的结果;

2.在执行异步操作的时候传入一个 java.nio.channels 。

操作完成后胡回调 CompletionHandler 接口的实现类。

NIO 2.0的异步套接字通道是真正的异步非阻塞I/O,对应于UNIX网络编程中的事件驱动I/O。

为什么选择Netty
不选择Java原生NIO编程的原因

以下内容引用自《Netty权威指南》:

NIO类库和API繁杂,使用麻烦,你需要熟练掌握 Selector , ServerSocketChannel 、 SocketChannel 、 ByteBuffer 等。
需要其他额外的技能做铺垫,例如熟悉Java多线程编程。这是因为NIO编程涉及 Reactor模式,你必须对多线程和网络编程十分熟悉,才能编写出高质量的NIO程序。
可靠性能力补齐 ,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理问题,NIO编程的特点是功能开发相对容易,可靠性能力补齐的工作量和难度巨大。
JDK NIO的BUG,例如臭名昭著的 epoll GUG ,它会导致Selector空轮询,最终导致CPU 100%。官方声称在1.6版本中修复,但是在1.7版本中该问题依然存在,只不过GUG发生的概率降低了很多。
为什么选择Netty
Netty是业界最流行的NIO框架之一, 它的健壮性、功能、性能、可定制性和可扩展性在同类框架中都是首屈一指的, 它已经得到成百上千的商用项目验证, 例如Hadoop的RPC框架Avro就使用了Netty作为底层通信框架, 其他还有业界主流的RPC框架, 也使用Netty来构建高性能的异步通信能力。

通过对Netty的分析,我们将它的优点总结如下。

API使用简单, 开发门槛低;
功能强大,预置了多种编解码功能,支持多种主流协议;
定制能力强, 可以通过 ChannelHandler 对通信框架进行灵活地扩展;
性能高, 通过与其他业界主流的NIO框架对比,Netty的综合性能最优;
成熟、稳定,Netty修复了已经发现的所有 JDK NIO BUG , 业务开发人员不需要再为NIO的BUG而烦恼;
社区活跃, 版本迭代周期短, 发现的BUG可以被及时修复,同时, 更多的新功能会加入;
经历了大规模的商业应用考验, 质量得到验证。Netty在互联网、大数据、网络游戏、企业应用、电信软件等众多行业已经得到了成功商用,证明它已经完全能够满足不同行业的商业应用了。
正是因为这些优点,Netty 逐渐成为了 Java NIO 编程的首选框架。
通过以上内容帮助大家回忆一下Linux网络编程中的IO模型,搞清楚同步异步阻塞非阻塞的概念,了解了BIO到NIO的区别和联系。最后我们知道了众多的NIO框架中,为什么要选择Netty。这些知识也是我们后续进行网络编程和Netty开发的基础。

以上就是有关Netty的有关内容,希望可以对大家学习Netty有帮助,喜欢的小伙伴可以帮LZ进行转发+关注,也会不定时更新干货!感谢大家!

点赞收藏
分类:标签:
1894474
请先登录,查看7条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步
7
7