远程调用模拟-用Socket实现与RMI的形式的对比

原创
2015/07/01 15:16
阅读数 172

远程调用其实没有想象地那么复杂,当然,性能好的RPC当然需要仔细设计,那么最基础的理解什么是RPC呢?从本DEMO看起:

暴露的服务:等待远程来调用。客户端在调用前需要来绑定生成个代理,类似于动态代理模式

import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
import java.net.ServerSocket;  
import java.net.Socket;  
  
/** 
 * RpcFramework 
 *  
 * @author william.liangf 
 */  
public class RpcFramework {  
  
    /** 
     * 暴露服务 
     *  
     * @param service 服务实现 
     * @param port 服务端口 
     * @throws Exception 
     */  
    public static void export(final Object service, int port) throws Exception {  
        if (service == null)  
            throw new IllegalArgumentException("service instance == null");  
        if (port <= 0 || port > 65535)  
            throw new IllegalArgumentException("Invalid port " + port);  
        System.out.println("Export service " + service.getClass().getName() + " on port " + port);  
        ServerSocket server = new ServerSocket(port);  
        for(;;) {  
            try {  
                final Socket socket = server.accept();  
                new Thread(new Runnable() {  
                    @Override  
                    public void run() {  
                        try {  
                            try {  
                                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());  
                                try {  
                                    String methodName = input.readUTF();  
                                    Class<?>[] parameterTypes = (Class<?>[])input.readObject();  
                                    Object[] arguments = (Object[])input.readObject();  
                                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());  
                                    try {  
                                        Method method = service.getClass().getMethod(methodName, parameterTypes);  
                                        Object result = method.invoke(service, arguments);  
                                        output.writeObject(result);  
                                    } catch (Throwable t) {  
                                        output.writeObject(t);  
                                    } finally {  
                                        output.close();  
                                    }  
                                } finally {  
                                    input.close();  
                                }  
                            } finally {  
                                socket.close();  
                            }  
                        } catch (Exception e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }).start();  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  
    /** 
     * 引用服务 
     *  
     * @param <T> 接口泛型 
     * @param interfaceClass 接口类型 
     * @param host 服务器主机名 
     * @param port 服务器端口 
     * @return 远程服务 
     * @throws Exception 
     */  
    @SuppressWarnings("unchecked")  
    public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {  
        if (interfaceClass == null)  
            throw new IllegalArgumentException("Interface class == null");  
        if (! interfaceClass.isInterface())  
            throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");  
        if (host == null || host.length() == 0)  
            throw new IllegalArgumentException("Host == null!");  
        if (port <= 0 || port > 65535)  
            throw new IllegalArgumentException("Invalid port " + port);  
        System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);  
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {  
            public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {  
                Socket socket = new Socket(host, port);  
                try {  
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());  
                    try {  
                        output.writeUTF(method.getName());  
                        output.writeObject(method.getParameterTypes());  
                        output.writeObject(arguments);  
                        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());  
                        try {  
                            Object result = input.readObject();  
                            if (result instanceof Throwable) {  
                                throw (Throwable) result;  
                            }  
                            return result;  
                        } finally {  
                            input.close();  
                        }  
                    } finally {  
                        output.close();  
                    }  
                } finally {  
                    socket.close();  
                }  
            }  
        });  
    }  
  
}

调用的接口:

/** 
 * HelloService 
 *  
 * @author william.liangf 
 */  
public interface HelloService {  
  
    String hello(String name);  
    String cry(String name,int minutes);  
  
}

接口的实现:

  
/** 
 * HelloServiceImpl 
 *  
 * @author william.liangf 
 */  
public class HelloServiceImpl implements HelloService {  
  
    public String hello(String name) {  
        return "Hello " + name;  
    }

	@Override
	public String cry(String name,int minutes) {
		// TODO Auto-generated method stub
		 return "Cry " + name+"minutes"+minutes;  
	}  
  
}

消费者:

/** 
 * RpcConsumer 
 *  
 * @author william.liangf 
 */  
public class RpcConsumer {  
      
    public static void main(String[] args) throws Exception {  
        HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);  
        for (int i = 0; i < Integer.MAX_VALUE; i ++) {  
            String hello = service.hello("World" + i);  
            System.out.println(hello);  
            
            if (i==2||i==1) {
            	 String Cry = service.cry("CryCryCry" + i,i);  
            	 System.out.println(Cry);  
			}
            
            Thread.sleep(1000);  
        }  
    }  
      
}

提供者:

/** 
 * RpcProvider 
 *  
 * @author william.liangf 
 */  
public class RpcProvider {  
  
    public static void main(String[] args) throws Exception {  
        HelloService service = new HelloServiceImpl();  
        RpcFramework.export(service, 1234);  
    }  
  
}

从代码中可以看出,该RPC调用的核心是SOCKET通信。

然后往Socket里写Object,轻松理解RPC

------------------------------------------------------------------------我是分割线

那么对比下另外一种远程调用:rmi 

服务的接口

import java.rmi.Remote;
import java.rmi.RemoteException;


public interface AddServer extends Remote { 
	public int AddNumbers(int firstnumber,int secondnumber) throws RemoteException; 
}

服务的实现

public class AddServerImpl extends UnicastRemoteObject implements AddServer { 
	public AddServerImpl() throws RemoteException { 
		super(); 
	} 
	public int AddNumbers(int firstnumber,int secondnumber) throws RemoteException { 
		return firstnumber + secondnumber; 
	} 
}

远程RMI的具体服务:

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

public class RmiServer { 
	 /**   
	    * 启动 RMI 注册服务并进行对象注册   
	    */   
	   public static void main(String[] argv)    
	   {    
	      try   
	      {    
	         //启动RMI注册服务,指定端口为1099 (1099为默认端口)    
	         //也可以通过命令 $java_home/bin/rmiregistry 1099启动    
	         //这里用这种方式避免了再打开一个DOS窗口    
	         //而且用命令rmiregistry启动注册服务还必须事先用RMIC生成一个占位程序(stub类)为它所用    
	         LocateRegistry.createRegistry(1099);    
	            
	         //创建远程对象的一个或多个实例,下面是hello对象    
	         //可以用不同名字注册不同的实例    
	         AddServer add = new AddServerImpl();    
	            
	         //把hello注册到RMI注册服务器上,命名为Hello    
	         Naming.rebind("Hello", add);    
	             
	         //如果要把hello实例注册到另一台启动了RMI注册服务的机器上    
	         //Naming.rebind("//192.168.1.105:1099/Hello",hello);    
	            
	         System.out.println("Hello Server is ready.");    
	      }    
	      catch (Exception e)    
	      {    
	         System.out.println("Hello Server failed: " + e);    
	      }    
	   }   
}

客户端:

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

import com.gjy.rmi.service.AddServer;

public class RmiClient { 
public static void main(String args[]) throws RemoteException, MalformedURLException, NotBoundException { 
		String url="rmi://127.0.0.1/Hello"; 
		AddServer add; 
		add = (AddServer)Naming.lookup(url); 
		int result=0;
		for (int i =0;i<10;i++){
		result = add.AddNumbers(10,i);
		System.out.println(result); 
		}
	} 
}

从以上可看出rmi的编码效率远大于socket的形式,节约了非常多的代码。下一遍文章会分析其他的远程调用并对比性能

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部