RMI是Java的一组拥护开发分布式应用程序的API。RMI使用Java语言接口定义了远程对象,它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。简单地说,这样使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方法调用,由于J2EE是分布式程序平台,它以RMI机制实现程序组件在不同操作系统之间的通信。比如,一个EJB可以通过RMI调用Web上另一台机器上的EJB远程方法。(来自百度百科) 公司的游戏项目服务端采用Java架构的,在实现游戏跨服通信采用的技术就是RMI技术。RMI在传输速度上虽然没有纯Socket方式快,但是接口简洁,学习门槛比较低,开发速度快。
RMI开发步骤:
1. 创建RMI接口对象,这是实现双方通信的规范,需要继承Remote接口
2. 创建一个具体的类来实现上边的通信接口,注意此类必须继承UnicastRemoteObject
3. 在服务端注册一个RMI服务,监听请求
4. 客户端通过JNDI( Java Naming and Directory Interface )找到目标服务,并调用远程方法
下边演示一个简单的例子。 (服务端与客户端只是主方法代码不同
首先为服务建立一个实体类,注意因为此对象需要现实进行远程传输,所以必须继承Serializable接口
public class Person implements Serializable{ private static final long serialVersionUID = 1L; private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString(){ return "姓名:"+name+","+"年龄:"+age; } }
public interface IPersonService extends Remote{ public List<Person> getPersonList(long sendTime) throws RemoteException; }
public class PersonService extends UnicastRemoteObject implements IPersonService{ public PersonService() throws RemoteException{ } @Override public List<Person> getPersonList(long recevieTime) throws RemoteException { List<Person> pList = new ArrayList<Person>(); Person p1 =new Person(); p1.setName("Lily"); p1.setAge(15); Person p2 =new Person(); p2.setName("Lucy"); p2.setAge(16); pList.add(p1); pList.add(p2); long now = System.currentTimeMillis(); System.err.println("服务端收到数据并处理,耗时毫秒数"+(now-recevieTime)); return pList; } }服务端测试代码
public class Test { public static void main(String[] args) throws Exception{ IPersonService service = new PersonService(); LocateRegistry.createRegistry(8890); Naming.bind("rmi://127.0.0.1:8890/PersonService", service); System.err.println("服务端开始监听请求。。。"); } }客户端测试代码
public class Test { public static void main(String[] args) throws Exception{ String rmiPath = "rmi://127.0.0.1:8890/PersonService"; System.err.println("客户端开始请求数据。。。"); long begin = System.currentTimeMillis(); IPersonService service = (IPersonService)Naming.lookup(rmiPath); //调用rmi接口所有方法都是阻塞的…… List<Person> list = service.getPersonList(begin); for(Person p:list){ System.err.println(p); } try{ //模拟处理其他逻辑 Thread.sleep(200); }catch(Exception e){ } long now = System.currentTimeMillis(); System.err.println("客户端接受数据并处理,耗时毫秒数:"+(now-begin)); } }
从运行结果可以看出,此通信方式为阻塞模式,客户端必须等服务端响应后才可以执行其他逻辑。
当客户端的请求次数达到一定规模的话,服务端会很容易遇到瓶颈。
改进方式:
1. 服务端采用线程池机制,并发处理请求
2. 修改通信方式为异步请求,客户端发出请求后继续执行其他逻辑而不等待,服务端定时处理来自客户端的请求队列并响应结果。通过回调的方式处理客户端的后续操作。