Java核心技术卷II(第8版) – 读书笔记 – 第10章

1、本章关注Java分布式技术,特别是用于两个Java虚拟机之间的远程方法调用RMI。

2、我们想要这样一种机制:客户端的程序员以常规方式进行方法调用,而无需关心在数据在网络上传输或者解析响应的问题(解决方法是在客户端上安装一个代理类,由他处理技术细节)。

3、类似的,服务器端也需要有这样的功能,让传输和业务逻辑分离,于是有了如下的结构:

客户端 <-调用本地方法、返回-> 代理 <-->代理 <-调用本地方法,返回-> 服务器

4、代理之间的通信,可以用RMI(Java远程过程调用)、COBRA等实现。

5、RMI是EJB所选用的通信协议,当然它只能运行于Java平台中。

6、COBRA、SOAP等则是完全独立于语言的。客户端、服务器端可以用C、C++、Java或其他任何支持的语言实现。但是一般都需要一个通用描述语言来屏蔽跨语言导致的不一致,如COBRA是IDL文件,SOAP是WSDL。

7、Web-Service完全构建于HTTP请求和XML之上,由于解析XML的效率问题,光环已经逐渐退去。。

8、远程代码调用:在一台机器(客户端)上的代码,需要调用另一台机器(服务器)上的某一个方法。

9、客户端使用的代理对象叫做stub(书上翻译成存根,真心别扭)。存根负责将客户所调用的代码参数打包成一组字节,这一过程称作参数编组。在RMI中,这一过程是用序列化实现的(序列化的包括远程对象标识符、被调用的方法描述、编组后的参数)。

10、在服务器端,负责:定位索要调用的远程对象(同一端口绑定的服务下,可以有多个远程对象),调用所需要的对象方法、返回值或者异常,将上述结果打包返回给客户端存根。

11、上述过程虽然很复杂,但对程序员来说是透明的。

12、首先来实现RMI的服务器端部分:

首先是实现一个接口,它必须实现了import java.rmi.Remote,注意每个方法必须都抛出RemoteException异常!

package server;
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Compute extends Remote {

	public int add(int a, int b) throws RemoteException;

	public int sub(int a, int b) throws RemoteException;

}

然后是实现上述服务器端接口的业务逻辑,它必须继承自UnicastRemoteObject,这是为了naming注册用的,实际上这两步是可以二合一的。

package server;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class ComputeImpl extends UnicastRemoteObject implements Compute {

	protected ComputeImpl() throws RemoteException {
		super();
	}

	@Override
	public int add(int a, int b) throws RemoteException {
		return a + b;
	}

	@Override
	public int sub(int a, int b) throws RemoteException {
		return a - b;
	}

}

如果我们不想extends自UnicastRemoteObject,则可以在对象中如下操作:

UnicastRemoteObject.exportObject(this, port);

13、在实现客户端的stub之前,我们还需要一个方法,让客户端可以定位到远程服务器的对象上,怎么搞呢?Java中最常用的就是注册名字方法。

RMI的URL以rmi://开头,后面跟着IP:Port/对象的名字,比如:

rmi://xx.xx.xx.xx:999/compute

下面的代码将服务器端的对象ComputeImpl注册到RMI的URL上。这部分代码其实才是真正的“服务器”绑定端口、IP那部分。

package server;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class ComputeServer {
	public static void main(String[] args) throws Exception {
		// Start Reg server
		LocateRegistry.createRegistry(9999);
		// Make object
		ComputeImpl comp_impl = new ComputeImpl();
		// Bind
		Naming.rebind("rmi://127.0.0.1:9999/Compute", comp_impl);
		System.out.println("Server Started");
	}
}

14、下面就是客户端的程序了,实际上,客户端可以Naming查找,直接使用上面我们声明的接口,这样就是“代理”了,这个思路确实非常好……

package client;

import java.rmi.Naming;
import server.Compute;

public class ComputeClient {

	public static void main(String[] args) throws Exception {
		Compute comp = (Compute) Naming.lookup("rmi://127.0.0.1:9999/Compute");  //comp is stub object
		System.out.println(comp.add(1, 3));
	}
}

15、RMI传递对象:理论上和传输普通值是一样的,但实际上发送者和接收者看到的对象是不在同一片内存中的。但是,只要可序列化的(实现了Serializable)的都可以传输。

16、远程调用比本地调用要慢很多很多。

17、stub对象的equals和hashCode无意义,它们只表示了在远程的位置,只要指向的对象相同,就认为它们的equals和hashCode相同。

18、同理,stub对象也无法clone,会抛异常。

19、RMI也支持激活(activation),即让对象延迟加载,这时需要继承Activatable而不是UnicastRemoteObject。

20、WebService这种已经半过气的技术略过。

本章完毕。

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *