IO网络编程(一)

IO网络编程入门(理论)

1.传统的BIO编程

网络编程的基本模型是 Client/Server 模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的 IP 地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过网络套接字(Socket)进行通信。 在基于传统同步阻塞模型开发中,ServerSocket 负责绑定 IP 地址,启动监听端口;Socket 负责发起连接操作。连接成功之后,双方通过输入和输出流进行同步阻塞式通信。
传统的BIO通讯模型
采用 BIO 通信模型的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。模型图如下:
在这里插入图片描述
缺点:该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈 1:1 的正比关系,由于线程是 Java 虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。

伪异步I/O编程

为了解决同步阻塞 I/O 面临的一个链路需要一个线程处理的问题,后来有人对它的线程模型进行了优化,后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数 M:线程池最大线程数 N 的比例关系,其中 M 可以远远大于 N,通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽。
**伪异步 I/O 模型:**当有新的客户端接入的时候,将客户端的 Socket 封装成一个 Task(该任务实现 java.lang.Runnable 接口)投递到后端的线程池中进行处理,JDK 的线程池维护一个消息队列和 N 个活跃线程对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。
在这里插入图片描述
伪异步 I/O 实际上仅仅只是对之前 I/O 线程模型的一个简单优化,它无法从根本上解决同步 I/O 导致的通信线程阻塞问题。下面我们就简单分析下如果通信对方返回应答时间过长,会引起的级联故障。

  1. 服务端处理缓慢,返回应答消息耗费60s,平时只需要10ms。
  2. 采用伪异步I/O的线程正在读取故障服务节点的响应,由于读取输入流是阻塞的,因此,它将会被同步阻塞60s。
  3. 假如所有的可用线程都被故障服务器阻塞,那后续所有的I/O消息都将在队列中排队。
  4. 由于线程池采用阻塞队列实现,当队列积满之后,后续入队列的操作将被阻塞。
  5. 由于前端只有一个Accptor线程接收客户端接入,它被阻塞在线程池的同步阻塞队列之后,新的客户端请求消息将被拒绝,客户端会发生大量的连接超时。
  6. 由于几乎所有的连接都超时,调用者会认为系统已经崩溃,无法接收新的请求消息。

NIO编程

NIO 到底是什么的简称?
有人称之为 New I/O,因为它相对于之前的 I/O 类库是新增的,所以被称为 NewI/O,这是它的官方叫法。但是,由于之前老的 I/O 类库是阻塞 I/O,New I/O 类库的目标就是要让 Java 支持非阻塞 I/O,所以,更多的人喜欢称之为非阻塞 I/O(Non-block I/O),由于非阻塞 I/O 更能够体现 NIO 的特点,所以本文使用的NIO 都指的是非阻塞 I/O。
与 Socket 类和 ServerSocket 类相对应,NIO 也提供了 SocketChannel 和ServerSocketChannel 两种不同的套接字通道实现。这两种新增的通道都支持阻塞和非阻塞两种模式。阻塞模式使用非常简单,但是性能和可靠性都不好,非阻塞模式则正好相反。开发人员一般可以根据自己的需要来选择合适的模式,一般来说,低负载、低并发的应用程序可以选择同步阻塞 I/O 以降低编程复杂度,但是对于高负载、高并发的网络应用,需要使用 NIO 的非阻塞模式进行开发。

AIO编程

NIO2.0 引入了新的异步通道的概念,并提供了异步文件通道和异步套接字
通道的实现。异步通道提供两种方式获取获取操作结果:
• 通过java.util.concurrent.Future类来表示异步操作的结果;
• 在执行异步操作的时候传入一个java.nio.channels;
• CompletionHandler接口的实现类作为操作完成的回调。
NIO2.0 的异步套接字通道是真正的异步非阻塞 I/O,它对应 UNIX 网络编程
中的事件驱动 I/O(AIO),它不需要通过多路复用器(Selector)对注册的通道进行轮询操作即可实现异步读写,从而简化了 NIO 的编程模型。

几种I/O模型对比

在这里插入图片描述

主流选择 Netty 的理由

Netty 是业界最流行的 NIO 框架之一,它的健壮性、功能、性能、可定制性
和可扩展性在同类框架中都是首屈一指的,它已经得到成百上千的商用项目验证,例如 Hadoop 的 RPC 框架 avro 使用 Netty 作为底层通信框架;很多其他业界主流的 RPC 框架,也使用 Netty 来构建高性能的异步通信能力。
通过对 Netty 的分析,我们将它的优点总结如下 :
• API使用简单,开发门槛低;
• 功能强大,预置了多种编解码功能,支持多种主流协议;
• 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
• 性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优;
• 成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦恼;
• 社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的
新功能会加入;
• 经历了大规模的商业应用考验,质量得到验证。在互联网、大数据、网络
游戏、企业应用、电信软件等众多行业得到成功商用,证明了它已经完全
能够满足不同行业的商业应用了。
正是因为这些优点,Netty 逐渐成为 Java NIO 编程的首选框架。

再总结一下为什么不建议开发者直接使用 JDK 的 NIO 类库进行开发,
具体原因如下。
1.NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
2.需要具备其他的额外技能做铺垫,例如熟悉Java多线程编程。这是因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序。
3.可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等问题,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐的工作量和难度都非常大。
4.JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该BUG发生概率降低了一些而已,它并没有被根本解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值