分布式Java应用的体系结构知识简单分为:
- 网络通信:包括协议和IO
- 消息方式的系统间通信:包括基于Java包、基于开源框架、性能角度
- 远程调用方式的系统间通信:包括基于Java包、基于开源框架、性能角度
大型应用拆分为多个子系统来实现,这些子系统可能部署在同一台机器,或者不同机器的多个不同JVM中,每个子系统对应一个JVM。但这些子系统又不是完全独立的,要相互通信来共同实现业务功能,对于此类Java引用,我们称之为分布式Java引用。通常有两种典型的方法来实现。
- 基于信息方式实现系统间的通信,通常基于网络协议来实现。
- 基于远程调用方式实现系统间的通信,通常基于RMI和WebService
消息方式
基于Java自身技术
基于Java自身包实现消息方式的系统间通信的方式有:
- TCP/IP+BIO
- TCP/IP+NIO
- UDP/IP+BIO
- UDP/IP+NIO
TCP/IP+BIO
在Java中可基于Socket、ServerSocket来实现TCP/IP+BIO的系统间通信。Socket用于实现建立连接及网络IO的操作,ServerSocket用于实现服务器端端口的监听及Socket对象的获取。这种简单的通信模式,运用到实际的系统中,通常需要面对的是客户端同时要发送多个请求到服务器端,服务器端则同时要接受多个连接发送的请求。 为了解决客户端同时要发送多个请求到服务器端的需求,解决办法是:
- 生成多个Socket,问题是消耗客户端过多的本地资源,消耗服务器端过多的连接数,连接耗时较长导致的系统性能下降。解决方法是采用连接池的方式来维护Socket。连接池的问题是Socket数量有限导致的竞争和等待和如何合理控制等待响应的超时时间。
为了满足服务端能同时接受多个连接发送的请求,解决办法是:
- 在accept获取Socket后,将此Socket方式放入一个线程中处理,称为一连接一线程。缺点是无论连接上是否有真实的请求,都要耗费一个线程。为防止过多线程导致的服务器端资源耗尽,必须限制创建线程的数量,BIO方式下服务器端所能支撑的连接数有限。
TCP/IP+NIO
在Java中可基于java.nio.channels中的Channel和Selector的相关类来实现TCP/IP+NIO方式的系统间通信。SocketChannel用于建立连接、监听事件及操作读写,ServerSocketChannel用于监听端口及监听连接事件,程序通过Selector来获取是否有要处理的事件。NIO是典型的Reactor模式的实现,通过注册感兴趣的事件及扫描是否有感兴趣的事件发生,从而做相应的动作。这种简单的通信模式,运用到实际的系统中,通常需要面对的是客户端同时要发送多个请求到服务器端,服务器端则同时要接受多个连接发送的请求。
为了解决客户端同时要发送多个请求到服务器端的需求,解决办法是:
- 采用与TCP/IP+BIO类似的方式,使用多个SocketChannel。但是NIO方式可做到不阻塞,如果服务器端返回的相应能够带上请求标识,客户端则可采用连接复用的方式。对于连接不复用的情况,可基于Socket.setSoTimeout的方式来控制同步请求的超时;对于连接复用的情况,同步请求的超时可基于BlockingQueue、对象的wait/notify机制或Future机制来实现。
为了解决服务端接受多个连接请求的需求,解决办法是:
- 由一个线程来监听连接的事件,另一个或多个线程来监听网络流读写的时间。当有实际的网络流读写事件发生后,再放入线程池中处理。这种方式称为一请求一线程,好处是可接受很多的连接,这些连接只在有真实的请求时才会创建线程来处理。非常适用于服务端需要支持大量的连接数,但这些连接同时发送的请求不会太多。
对于高访问量的系统而言,TCP/IP+NIO方式结合一定的改造在客户端能够带来更高的性能,在服务端能支持更高的连接数。
UDP/IP+BIO
Java对UDP/IP方式的网络数据传输同样采用Socket机制,只是没有建立连接的要求,同时无法双向通信,除非两端都是UDP Server。在Java中可基于DatagramSocket和DatagramPacket来实现UDP/IP+BIO方式的系统间通信,DatagramSocket负责监听端口及读写数据。DatagramPacket作为数据流对象进行传输。
UDP/IP通信方式的两端不建立连接,不会有TCP/IP通信连接竞争的问题,只是最终读写流的动作是同步的。客户端同时发送多个消息的请求使用多个DatagramPacket即可。服务端同时接收多个请求的需求,采用没接收到一个packet就放入一个线程中进行处理的方式来实现。
UDP/IP+NIO
在Java中通过DatagramChannel和ByteBuffer来实现UDP/IP+NIO方式的系统间通信,DatagramChannel负责监听端口及进行读写,ByteBuffer则用于数据流传输。
对于UDP/IP方式,NIO带来的好处是只在有流要读取或可写入流时才做相应的IO操作,而不用像BIO方式直接阻塞当前线程。
多播的实现
以上都是基于Java包的一对一的系统间通信方式,实际场景中,可能需要将消息发送给多台机器,针对此场景有两种解决办法:
- 为每个目标机器建立一个连接,缺点是对消息发送端造成很大的网络流量压力
- 利用基于UDP/IP扩展出来的多播协议,在Java中可基于MulticastSocket和DatagramPakcet来实现多播网络通信。在多播通信中,接收数据端加入多播组来进行数据的接收,发送数据也要求加入多播组进行发送,多播的目标地址具有指定的地址范围,在224.0.0.0和239.255.255.255之间。
在Java应用中,多播通常用于多台机器的状态的同步。
基于开源框架
Mina是Apache的顶级项目,基于Java NIO构建,同时支持TCP/IP和UDP/IP两种协议,Mina对外屏蔽了Java NIO使用的复杂性,并在性能上做了不少优化。
在使用Mina时,关键的类为IoConnector、IoAcceptor、IoHandler及IoSession,Mina采用Filter Chain的方式封装消息发送和接收的流程,在这个Filter Chain的过程中可进行消息的处理、消息的发送和接收等。
- IoConnector 负责配置客户端的消息处理器、IO事件处理线程池、消息发送/接收的Filter Chain 等。
- IoAcceptor 负责配置服务器端的IO事件处理线程池、消息发送/接收的Filter Chain等。
- IoHandler 作为Mina和应用的接口,当发生了连接事件、IO事件或异常事件时,Mina都会通知应用所实现的IoHandler。
- IoSession 类似于SocketChannel的封装,不过Mina对连接做了进一步的抽象,因此可进行更多连接的控制及流信息的输出。
除了Mina之外,现在JBoss Netty也是现在一个广受关注的Java通信框架,据评测JBoss Netty的性能浩宇Mina。
小结
使用Java包来实现基于消息方式的系统间通信还是比较麻烦。为了让开发人员更加专注于对数据进行业务处理,而不用过多关注纯技术细节,开源业界诞生了很多优秀的基于以上各种协议的系统间通信的框架,比如Mina。