1、RMI 简介
Remote Method Invocation,远程方法调用,是一种技术,基于JRMP(Java Remote MessagingProtocol)协议
是Java分布式应用的基础,EJB的基石,构建于TCP之上的应用层协议,仅仅适用于java语言编写的应用程序之间通讯。
在RMI协议中,对象使用序列化机制编码。
2、RMI 开发示例
- 定义远程服务接口:必须继承 Remote 接口
- 定义远程服务实现类:必须继承 UnicastRemoteObject 类
- 注册RMI服务
- 客户端使用RMI服务
2.1 定义远程服务接口
package com.yli.rmi.server;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Map;
/**
* 定义远程服务对象接口 UserService,必须继承 , 方法需要抛出 RemoteException异常<br>
* 1.方法抛出异常是为了客户端能捕获异常<br>
* 2.客户端需要定义一模一样的接口,包结构也得一样<br>
* 因此一般是服务方提供jar包给客户端调用,客户端不需要重新定义业务接口
*/
public interface UserService extends Remote {
Map<String, Object> queryUser(String userId) throws RemoteException;
}
2.2 定义远程服务实现类
package com.yli.rmi.server;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
/**
* 定于远程服务实现类,必须继承UnicastRemoteObject <br>
* 由于RMI协议传输数据是依靠对象的序列化机制<br>
* 因此 UnicastRemoteObject 其顶层父类实现了Serializable接口<br>
* 这就要求你定义的服务类也要给予序列化Id,在Eclipse工具里面可自动生成<br>
* 也可以借助 jdk\bin\serialver.exe 工具来时限
*/
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
protected UserServiceImpl() throws RemoteException {
super();
}
private static final long serialVersionUID = 6269131066149250252L;
@Override
public Map<String, Object> queryUser(String userId) throws RemoteException {
String request = String.format("----->服务端收到客户端请求:userId=%s,timestamp=%s", userId, System.currentTimeMillis());
System.out.println(request);
Map<String, Object> user = new HashMap<String, Object>();
user.put("userId", userId);
user.put("userName", "test");
return user;
}
}
2.3 注册RMI服务
package com.yli.rmi.server;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
* 注册RMI服务
*/
public class RmiServiceRegister {
public static void main(String[] args) throws RemoteException, NamingException {
/**
* RMI 协议url格式 rmi://host[:port]/serviceName <br>
* 比如注册一个服务:rmi://abc.com/queryUserInfo <br>
* 如果在本机测试,比如使用:rmi://localhost:1099/queryUserInfo <br>
*/
bind1();
// bind2();
}
public static void bind1() throws RemoteException, NamingException {
System.out.println("**** beging register queryUserService ****");
int port = 8888;
String rmiUrl = "rmi://localhost:" + port + "/queryUserService";
// 注册RMI服务方监听端口
LocateRegistry.createRegistry(port);
// 绑定服务
UserServiceImpl uimpl = new UserServiceImpl();
Context namingCxt = new InitialContext();
namingCxt.bind(rmiUrl, uimpl);
// 重新绑定则会覆盖已经存在的服务
// namingCxt.rebind(rmiUrl, uimpl);
System.out.println("**** end register ****");
}
public static void bind2() throws RemoteException, NamingException {
System.out.println("**** beging register queryUserService ****");
// 设置上下文
Hashtable<String, String> env = new Hashtable<String, String>();
// 初始化上下文
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
// 指定URL
int port = 8888;
env.put(Context.PROVIDER_URL, "rmi://localhost:" + port);
// 注册RMI
LocateRegistry.createRegistry(port);
// 绑定服务
UserServiceImpl uimpl = new UserServiceImpl();
Context context = new InitialContext(env);
// 通过hashtable已经把 url 初始化到Context了
// 此处只要绑定服务名称即可
context.bind("queryUserService", uimpl);
// 重新绑定则会覆盖已经存在的服务
// context.rebind("queryUserService", uimpl);
System.out.println("**** end register ****");
}
}
2.4 客户端使用RMI服务
package com.yli.rmi.client;
import java.rmi.RemoteException;
import java.util.Map;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.yli.rmi.server.UserService;
/**
* RMI客户端测试
*/
public class TestRmiClient {
public static void main(String[] args) throws NamingException, RemoteException {
// RMI 服务地址
String rmiUrl = "rmi://localhost:8888/queryUserService";
Context namingCxt = new InitialContext();
// 查找远程RMI服务
UserService us = (UserService) namingCxt.lookup(rmiUrl);
Map<String, Object> user = us.queryUser("1001");
System.out.println(user);
}
}