簡單實現Java的RMI——遠端方法呼叫
一、RMI簡介:
說到RMI就不得不說RPC了。
RPC:(Remote Procedure Call),遠端過程呼叫。
RMI(Remote Method Invocation),遠端方法呼叫。
RPC和RMI是有區別的,RPC中是通過網路服務協議向遠端主機發送請求,RPC遠端主機就去搜索與之相匹配的類和方法,找到後就執行方法並把結果編碼,通過網路協議發回。
而RMI是通過客戶端的物件作為遠端介面進行遠端方法的呼叫。RMI只適用於Java語言。
二、RMI的執行機理:
涉及兩個網路端。其核心思想是,一個端可以通過呼叫另一個端的方法,實現相關功能。
一個端“執行”一個方法,而這個方法的實際執行是在另一端進行的!
當然,兩個端都應該有相同的類,自然會擁有相同的方法。
一個端所謂的執行這個方法,其實是通過呼叫這個類的代理物件的方法,在其中攔截這個方法,在這個方法中
實際上是將執行這個方法的引數和類名稱、方法名稱,通過網路通訊傳輸給另一端;另一端根據得到的方法名稱、
類名稱和引數,實際執行那個方法,再將方法執行結果回傳給對端。
要注意的問題:
1、實際執行方法的一端,我們可以認為是RMI伺服器端,偽執行一端,自然是RMI客戶端;
2、偽執行端不應該自己完成引數、方法名稱和類名稱的傳遞工作;也就是說,對於RMI客戶端使用者而言,他只面對一個類的一個方法,
直接執行就好;
3、RMI伺服器端可能接收多個RMI客戶端有關這個方法的執行請求,每個客戶端的執行當然應該是獨立的,應該用執行緒實現;
4、RMI伺服器端在執行了相關方法,並回傳方法執行結果後,應該斷開與RMI客戶端的連線。
下面是我要實現它的一個思路。
1.首先:應該是RpcBeanDefinition:
1 package com.xupt.rpc.core;2 3 import java.lang.reflect.Method; 4 5 public class RpcBeanDefination { 6 7 private Class<?> klass; 8 private Method method; 9 private Object object; 10 11 RpcBeanDefination() { 12 }
給該類所有的成員都有getter和setter方法就不需要說了,這個類,將執行的哪個類的哪個方法和類的物件封裝起來,以後這個類將形成Map
的值,下面來介紹的RpcBeanFactory會重點介紹。
2.RpcBeanFactory
1 package com.xupt.rpc.core; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 public class RpcBeanFactory { 7 8 private final Map<String, RpcBeanDefination> beanMap; 9 10 RpcBeanFactory() { 11 beanMap = new HashMap<>(); 12 } 13 14 void rpcBeanRegistry(String beanId,RpcBeanDefination defination) { 15 RpcBeanDefination rbd = beanMap.get(beanId); 16 if(rbd != null) { 17 return; 18 } 19 beanMap.put(beanId, defination); 20 } 21 22 RpcBeanDefination getBean(String beanId) { 23 return beanMap.get(beanId); 24 } 25 }
此類是將序列號作為Map中的鍵,RpcBeanDeifintion作為值放入Map中,用BeanId來找對應客戶端那邊序列號相同的方法。
3.下來是RpcBeanRegistry:
1 package com.xupt.rpc.core; 2 3 import java.lang.reflect.Method; 4 5 public class RpcBeanRegistry { 6 7 RpcBeanRegistry() { 8 } 9 10 //給客戶端提供 11 static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces) { 12 doregistry(rpcBeanFactory,interfaces,null); 13 } 14 15 //內部使用,註冊 16 private static void doregistry(RpcBeanFactory rpcBeanFactory , Class<?> interfaces ,Object object) { 17 //得到介面中的所有的方法,行程方法的陣列 18 Method[] methods = interfaces.getDeclaredMethods(); 19 for(Method method : methods) { //遍歷這些方法 20 String beanId = String.valueOf(method.toString().hashCode());//將方法序列化。 21 RpcBeanDefination rpcBeanDefination = new RpcBeanDefination(); 22 23 //將得到的實現介面的那個類和它的方法以及物件放進RpcBeanDefination()中。 24 rpcBeanDefination.setKlass(interfaces); 25 rpcBeanDefination.setMethod(method); 26 rpcBeanDefination.setObject(object); 27 28 rpcBeanFactory.rpcBeanRegistry(beanId, rpcBeanDefination); 29 } 30 } 31 32 //服務端使用,知道實現類的物件。 33 static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces,Object object) { 34 //判斷此類是否實現了這個介面。 35 if(!interfaces.isAssignableFrom(object.getClass())){ 36 return; 37 } 38 doregistry(rpcBeanFactory,interfaces,object); 39 } 40 41 //伺服器端使用,知道類,建立一個物件。 42 static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces,Class<?> klass) { 43 //判斷該類是否實現了介面。 44 if(!interfaces.isAssignableFrom(klass)){ 45 return; 46 } 47 try { 48 doregistry(rpcBeanFactory, interfaces, klass.newInstance()); 49 } catch (Exception e) { 50 e.printStackTrace(); 51 } 52 } 53 }
這個類是同時給客戶端和伺服器使用的,所以有一個私有方法來完成類方法的獲得和序列號的產生(method.toString().hashcode())。然後將類、方法、物件放進RpcBeanDefinition中,將得到的序列號作為鍵和rpcBeanDeifinyion作為值放進Map中,形成鍵值對,方便客戶端和伺服器的呼叫。
4.下來是RpcServer:
1 package com.xupt.rpc.core;
2 3 import java.io.IOException; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 public class RpcServer implements Runnable { 8 9 private ServerSocket server; 10 private int port; 11 private boolean goon; 12 private final RpcBeanFactory rpcBeanFactory; 13 private static long executorId; 14 15 public RpcServer() { 16 rpcBeanFactory = new RpcBeanFactory(); 17 this.goon = false; 18 } 19 20 public void setPort(int port) { 21 this.port = port; 22 } 23 24 public void startRpcServer() throws Exception { 25 if(this.port == 0) { 26 return; 27 } 28 server = new ServerSocket(port); 29 this.goon = true; 30 new Thread(this,"Rpc_Server").start();//啟動執行緒 31 } 32 33 public void stopRpcServer() { 34 if (this.server != null && !this.server.isClosed()) { 35 try { 36 this.server.close(); 37 } catch (IOException e) { 38 e.printStackTrace(); 39 } finally { 40 this.server = null; 41 } 42 } 43 } 44 45 RpcBeanFactory getRpcBeanFactory() { 46 return rpcBeanFactory; 47 } 48 49 //用註冊的方法知道類實現的介面,類的物件,通過物件執行類的方法。 50 public void rpcRegistry(Class<?> interfaces,Object object) { 51 RpcBeanRegistry.registryInterface(rpcBeanFactory, interfaces, object); 52 } 53 54 public void rpcRegistry(Class<?> interfaces, Class<?> imClass) { 55 RpcBeanRegistry.registryInterface(rpcBeanFactory, interfaces,imClass); 56 } 57 58 @Override 59 public void run() { 60 while(goon) { 61 try { 62 Socket socket = server.accept();//不斷的偵聽 63 64 new RpcServerExecutor(socket, this,++executorId); 65 66 } catch (Exception e) { 67 goon = false; 68 e.printStackTrace(); 69 } 70 } 71 stopRpcServer(); 72 } 73 }