1. 程式人生 > >簡單實現Java的RMI——遠端方法呼叫

簡單實現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 }