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

举报
yd_245979989 发表于 2023/09/01 17:40:39 2023/09/01
【摘要】 NettyNetty是一款非常优秀的网络编程框架,是对NIO的二次封装,本文将重点剖析Netty客户端的启动流程,深入底层了解如何使用NIO编程客户端。Linux网络编程5种IO模型根据UNIX网络编程对于IO模型的分类,UNIX提供了5种IO模型,分别是 阻塞IO 、 非阻塞IO、 IO复用 、 信号驱动IO 、 异步IO 。这几种IO模型在《UNIX网络编程》中有详解,这里作者只简单介绍...

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。


非阻塞IO

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


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


信号驱动IO

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


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


用漫画帮你分清同步异步阻塞非阻塞

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





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

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

从BIO到AIO

BIO通信模型

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


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

为异步IO模型

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


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进行转发+关注,也会不定时更新干货!感谢大家!

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。