分布式Java读书笔记一:通信方式

《分布式Java应用》

image

第1章 分布式Java应用

1.基于消息方式实现系统间的通信

分布式环境下肯定需要多台机器相互调用,那么通信方式有哪些?
image.png
TCP/IP是一种可靠的网络数据传输的协议。

TCP/IP要求通信双方首先建立连接,之后再进行数据的传输。

UDP/IP是一种不保证数据一定到达的网络数据传输协议。UDP/IP并不直接给通信的双方建立连接,而是发送到网络上进行传递。

TCP/IP和UDP/IP可用于完成数据的传输,但要完成系统间通信,还需要对数据进行处理。例如读取和写入数据,按照POSIX标准分为同步IO和异步IO两种,其中同步IO中最常用的是BIO(Blocking IO)和NIO(Non-Blocking IO)。

BIO就是当发起IO的读或写操作时,均为阻塞方式,只有当程序读到了流或将流写入操作系统后,才会释放资源。

NIO是基于事件驱动思想的,实现上通常采用Reactor模式,从程序角度而言,当发起IO的读或写操作时,是非阻塞的;当Socket有流可读或可写入Socket时,操作系统会相应地通知应用程序进行处理,应用再将流读取到缓冲区或写入操作系统

AIO为异步IO方式,同样基于事件驱动思想,实现上通常采用Proactor模式。从程序角度而言,和NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。

2. 基于远程调用方式实现系统间的通信

Java中主要用来实现基于RMI和WebService的应用。

1.1 基于消息方式实现系统间的通信

基于Java自身技术实现消息方式的系统间通信

基于Java自身包实现消息方式的系统间通信的方式有:TCP/IP+BIO、TCP/IP+NIO、UDP/IP+BIO以及UDP/IP+NIO 4种,

TCP/IP+BIO

在Java中可基于Socket、ServerSocket来实现TCP/IP+BIO的系统间通信。Socket主要用于实现建立连接及网络IO的操作,ServerSocket主要用于实现服务器端端口的监听及Socket对象的获取

TCP/IP+NIO

在Java中可基于java.nio.channels中的Channel和Selector的相关类来实现TCP/IP+NIO方式的系统间通信。Channel有SocketChannel和ServerSocketChannel两种,SocketChannel用于建立连接、监听事件及操作读写,ServerSocketChannel用于监听端口及监听连接事件;程序通过Selector来获取是否有要处理的事件。
NIO是典型的Reactor模式的实现,通过注册感兴趣的事件及扫描是否有感兴趣的事件发生,从而做相应的动作。
在基于Sun JDK开发Java NIO程序时,尤其要注意selector.select抛出IOException异常的处理及selector.select不阻塞就直接返回的情况。这两种状况都有可能造成CPU消耗达到100%,对于selector.select抛出IOException的状况,可以采用绕过的方法为捕捉异常并将当前Thread sleep一段时间,或是重新创建Selector。为避免selector.select不阻塞就直接返回,可采用bug库中提到的修改建议。

UDP/IP+BIO

Java对UDP/IP方式的网络数据传输同样采用Socket机制,只是UDP/IP下的Socket没有建立连接的要求,由于UDP/IP是无连接的,因此无法进行双向的通信。这也就要求如果要双向通信的话,必须两端都成为UDP Server。

在Java中可基于DatagramSocket和DatagramPacket来实现UDP/IP+BIO方式的系统间通信, DatagramSocket负责监听端口及读写数据。DatagramPacket作为数据流对象进行传输

UDP/IP+NIO

在Java中可通过DatagramChannel和ByteBuffer来实现UDP/IP+NIO方式的系统间通信, DatagramChannel负责监听端口及进行读写,ByteBuffer则用于数据流传输。

在网络协议上还有一个基于UDP/IP扩展出来的多播协议,多播协议的传输方式是一份数据在网络上进行传输,而不是由发送者给每个接收者都传一份数据,这样,网络的流量就大幅度下降了。

在Java中可基于MulticastSocket和DatagramPacket来实现多播网络通信,MulticastSocket是基于DatagramSocket派生出来的类,其作用即为基于UDP/IP实现多播方式的网络通信

1.1.2 基于开源框架实现消息方式的系统间通信

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对连接做了进一步的抽象,因此可进行更多连接的控制及流信息的输出。

1.2 基于远程调用方式实现系统间的通信

1.2 基于远程调用方式实现系统间的通信

1.2.1 基于Java自身技术实现远程调用方式的系统间通信

在Java中实现远程调用方式的技术主要有RMI和WebService两种,

RMI(Remote Method Invocation)是Java用于实现透明远程调用的重要机制。在远程调用中,客户端仅有服务器端提供的接口。通过此接口实现对远程服务器端的调用。


image.png

图1.1 RMI机制
Sun JDK 6.0以前版本中的RMI实现均是基于TCP/IP+BIO方式的。

RMI要求服务器端的接口继承Remote接口,接口上的每种方法必须抛出RemoteException,服务器端业务类通过实现此接口提供业务功能,然后通过调用UnicastRemoteObject.exportObject来将此对象绑定到某端口上,最后将此对象注册到本地的LocateRegistry上,此时形成一个字符串对应于对象实例的映射关系。基于RMI实现示例中的服务器端代码如下。

RMI的客户端首先通过LocateRegistry.getRegistry来获取Registry对象,然后通过Registry.lookup字符串获取要调用的服务器端接口的实例对象,最后以接口的方式透明地调用远程对象的方法。按照以上描述,基于RMI实现客户端的关键代码如下:

Registry registry=LocateRegistry.getRegistry("localhost");      String name="BusinessDemo";    // 创建BusinessDemo类的代理类,当调用时则调用localhost:1099上名称为BusinessDemo的对     // 象,如服务器端没有对应名称的绑定,则抛出NotBoundException;如通信出现错误,则抛出    RemoteException    Business business=(Business) registry.lookup(name);
WebService是一种跨语言的系统间交互标准。

在这个标准中,对外提供功能的一方以HTTP的方式提供服务,该服务采用WSDL(Web Service Description Language)描述,在这个文件中描述服务所使用的协议、所期望的参数、返回的参数格式等。调用端和服务端通过SOAP(Simple Object Access Protocol)方式来进行交互。

image.png
WebService调用过程

Java SE6中集成了WebService,因此可以直接实现该方式的远程调用,服务器端通过@WebService来标记对外暴露的WebService实现类,通过调用Endpoint.publish将此WebService实现发布到指定的HTTP地址上。客户端通过wsimport来访问相应地址的wsdl文件,从而生成调用服务器端的辅助类,应用即可通过调用此类来实现远程调用了

对外暴露的接口:

public interface Business {      /**      * 显示客户端提供的信息,并返回      */      public String echo(String message);    }

服务器端的实现类,通过@WebService来指定对外提供的WebService的名称和客户端生成的类名及包名:

    @WebService(name="Business",serviceName="BusinessService",targetNamespace="htt    p://WebService.chapter1.book/client")    @SOAPBinding(style=SOAPBinding.Style.RPC)    public class BusinessImpl implements Business {            public String echo(String message) {                if("quit".equalsIgnoreCase(message.toString())){                    System.out.println("Server will be shutdown!");                    System.exit(0);                }                System.out.println("Message from client: "+message);                return "Server response:"+message;            }    }

发布WebService的类:

   public static void main(String[] args) {            Endpoint.publish("http://localhost:9527/BusinessService", new    BusinessImpl());            System.out.println("Server has beed started");    }

客户端通过JDK bin目录下的wsimport命令来生成辅助调用代码,执行如下命令生成辅助代码:
wsimport -keep http://localhost:9527/BusinessService?wsdl

执行后,在当前目录下会生成book/chapter1/WebService/client/Business.java和book/chapter1/WebService/client/BusinessService.java的代码,基于这两个生成的代码编写客户端的关键代码如下:
```
BusinessService businessService=new BusinessService(); Business business=businessService.getBusinessPort(); business.echo(command);

WebService传输的数据协议采用SOAP,SOAP对于复杂的对象结构比较难支持,其好处是能够支持跨语言。 

你可能感兴趣的:(分布式Java读书笔记一:通信方式)