网络编程BIO、伪异步IO

       随着JavaI/O类库的不断发展和改进,基于网络编程会变得越来越简单;随着异步I/O功能的增强,基于Java  NIO开发的网络服务器甚至不逊于采用C++开发的网络程序。

       一、传统BIO编程

       网络编程的基本模型是Client/Server模型,也就是两个进程之间进行通信(服务端和客户端分别是一个进程),其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接成功,双方就可以通过网络套接字(Socket)进行通信。

       在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功之后,双发通过输入和输出流进行同步阻塞式通信。

       1、BIO通信模型图

       通过下图的通信模型图来熟悉BIO的服务端通信模型:采用BIO通信的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。

       

         该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,由于线程并不是取之不尽用之不竭的,相反,它是JVM中非常宝贵的资源,当线程数无限制增大之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。

         BIO主要的问题在于,每当有一个新的客户端请求接入时,服务端必须创建一个新的线程来处理新接入的客户端链路,一个线程只能处理一个客户端连接。在高性能服务器应用领域,往往要面向成千上万个客户端的并发连接,这种模型显然无法满足高性能、高并发接入的场景。

         二、伪异步I/O编程

        为了改进传统I/O编程一线程一连接模型(即一个客户端链路需要一个线程处理的问题),后来有人对它进行优化,演变成一种通过线程池或者消息队列实现1个或多个线程处理N个客户端连接的模型,由于它的底层通信机制仍然会用同步阻塞I/O,所以被称为“伪异步”。

        服务端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M与线程池最大线程数N的比例关系,其中M可以远远大于N。通过线程池可以灵活地调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽。

       通过下图的通信模型图分析,当有新的客户端接入时,将客户端的Socket封装成一个Task(该Task实现Java.lang.Runnable接口)投递到后端的线程池中,调用线程池的execute方法进行处理,JDK的线程池维护一个消息队列和N个活跃线程数,对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大的线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。避免了每个请求接入都创建一个新的线程。相对于传统的一连接一线程模型,是一种改良。

         伪异步I/O通信框架采用了线程池实现,因此避免了每个请求都创建一个独立线程造成的线程资源耗尽问题。但由于它底层的通信仍然采用同步阻塞模型,因此,并没有从根本上解决问题。



伪异步I/O弊端分析:

        当对Socket的输入流进行读取操作的时候,它会一致阻塞下午,知道发生如下三种事件。

       ①有数据可读

       ②可用数据已经读取完毕

       ③发生空指针或者I/O异常

       这意味着当对方发送请求或者应答消息比较缓慢,或者网络传输较慢时,输入输入流一方的通信线程将被长时间阻塞,如果对方要60s才能够将数据发送完成,读取一方的I/O线程也将会被同步阻塞60s,在此期间,其他接入消息只能在消息队列中排队。






猜你喜欢

转载自blog.csdn.net/qq_26545305/article/details/79072220