大型应用通常会拆分为多个子系统,对于java来说,这些子系统可能部署在同一台机器上的多个不同的JVM中,也可能部署在不同的 电脑上,但这些子系统有不是完全独立的,要相互通信来实现业务功能,对于此类java应用,我们称为java分布式应用。
对于分布式java应用,通常有两种典型的方式来实现:
1.基于消息方式实现系统间的通信
当系统之间要通信时,就向外发送消息,消息可以是字节流、字节数组,甚至是java对象。
消息方式的系统间通信,通常基于网络协议来实现,常用的实现系统间通信的协议是:TCP/IP和UDP/IP
TCP/IP是一种可靠的网络数据传输协议,TCP/IP要求通信双方首先建立连接,然后进行通信。TCP/IP负责保证数据传输的可靠性,
包括数据的可到达、数据到达的顺序等,但由于TCP/IP要保证连接和数据的可靠性,因此可能会牺牲一些性能。
UDP/IP是一种不保证一定数据一定到达的网络数据传输协议。UDP/IP并不直接给通信的双方建立连接,而是发送到网络上进行传递。
由于UDP/IP不建立连接,并且不能保证数据传输的可靠性,因此性能上表现比较好,但可能出现数据丢失以及数据乱序的现象。
TCP/IP和UDP/IP可用于完成数据的传输,但要完成系统间的通信,还需要对数据进行处理。读取和写入,按照POSIX(可移植操作系统接口) 分为同步IO和异步IO,其中同步IO中最常用的是BIO(Blocking IO)和NIO(Non-Blocking IO)
BIO:当发起IO的读和写操作时,均为阻塞方式
NIO:是基于事件驱动的,发起IO的读和写操作时,均为非阻塞方式
AIO:为异步IO方式,同样基于事件驱动思想,和NIO不同,当进行读写操作时,只需直接调用API的read和write方法即可。这两种方法均为 异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。
较之NIO而言,AIO一方面简化了程序的编写,流的读取和写入都由操作系统来代替完成;另一方面省去了NIO中程序要遍历事件通知 队列(Selector)的代价。
windows基于IOCP实现了AIO,Linux目前基于epoll模拟实现了AIO
java对TCP/IP和UDP/IP都支持,在网络IO的操作上,java 7以前的版本只支持BIO和NIO,java 7以后的版本才支持AIO
实现的4种方式:TCP/IP+BIO,TCP/IP+NIO,UDP/IP+BIO,UDP/IP+NIO
TCP/IP+BIO
基于socket和serverSocket来实现
通常要面对客户端同时要发送多个请求到服务器端,服务器端同时要接受多个连接发送的请求
a.为了满足客户端同时发送多个请求到服务器端,最简单的方式就是产生多个socket。但是这样会产生两个问题:
一是生成太多的Socket会消耗过多的本地资源,并且客户端生成过多的Socket会导致服务器需要支撑非常高的连接数;
二是生成Socket通常是比较慢的,因此频繁的创建导致系统性能不足
鉴于这两个问题,通常采用连接池的方式来维护Socket,一方面限制了能创建Socket的个数;另一方面由于将socket放入池中,避免了重复创建socket带来的性能下降问题.
另一问题连接池中的socket个数是有限的,但同时要用socket的请求会很多,这时候就会造成激烈的竞争和等待,这时就要合理控制等待响应的超时时间(setSocketTimeOut)
b.为了满足服务器能同时接受多个连接发送的请求,通常采用的方式是在accept获取Socket之后,将此socket放入一个线程中处理,通常将此方式称为一连接一线程。这样服务器就可以接受多个连接发送请求了。
缺点:无论连接上是否有真实的请求,都要耗费一个线程。为避免创建过多的线程导致服务器端资源耗尽,须限制创建线程的数量。
这就造成了在采用BIO的情况下服务器端所能支撑的连接数是有限的。
TCP/IP+NIO
a.对于客户端发送多个请求的需求,NIO方式可以做到不阻塞,因此如果服务器返回的响应能带上请求标示,那客户端可以采用连接复用的方式,即每个SocketChannel在发送消息后,不用等响应即可继续发送其他消息,这种方式可降低连接池带来的资源争抢问题,从而提升系统性能;对于连接不复用的情况,可基于Socket.setSoTimeout的方式来控制同步请求的超时。
b.对于服务器端接受多个连接请求的需求,通常采用一个线程监听连接的事件,另一个或多个线程来监听网络流读写的事件
开源框架Mina,JBoss Netty提供了对TCP/IP+NIO通信的支持
UDP/IP+BIO
在java中可基于DatagramSocket和DatagramPacket来实现UDP/IP+BIO,DatagramSocket负责监听端口及读取数据。DatagramPacket作为数据流对象进行传输。
UDP/IP+NIO
在java中可基于DatagramChannel和ByteBuff来实现UDP/IP+NIO,DatagramChannel负责监听端口及读取数据。ByteBuff用于数据流传输。
2.基于远程调用方式实现系统间的通信
可通过调用一个本地的java接口的方法,透明的调用远程的java实现。具体细节则由java或框架来完成,这种方法主要用来实现基于RMI 和webservice的应用。
RMI的开源框架Spring RMI,webservice开源框架CXF,Axis等