1. 程式人生 > >自行實現一個簡單的RMI

自行實現一個簡單的RMI

在教主的帶領下,完成了一個很簡單的RMI工具。就當是練習網路程式設計吧。

RMI(Remote Method Invocation),遠端方法呼叫。

涉及兩個網路端。其核心思想是,一個端可以通過呼叫另一個端的方法,實現相關功能。 一個端“執行”一個方法,而這個方法的實際執行是在另一端進行的!

要進行遠端方法呼叫,那麼,兩個端都應該有相同的類,相同的方法。一個端執行一個方法,其實本質是通過呼叫這個類的代理物件的方法,在其中攔截這個方法,將這個方法的雜湊值和引數,通過網路通訊傳輸給另一端;另一端根據雜湊值和引數,能唯一的確定到那個方法,執行完成後將結果返回給對端。

我採用的是短連線,jdk代理。

大致的處理流程:

 我的思路:

建立RPC伺服器端:

主要有兩個大的步驟:

1、需要給RpcBeanFactory注入方法的雜湊值和方法引數(這兩個資料相同,就能保證客戶機準確的呼叫遠端方法)。

注入的具體分析:

static void doRegist(RpcBeanFactory rpcBeanFactory, Class<?> interfaces, Object object) {
        Method[] methods  = interfaces.getDeclaredMethods();
        for (Method method : methods) {
            String rpcBeanId = String.valueOf(method.toString().hashCode());
            RpcBeanDefination rpcBeanDefination = new RpcBeanDefination();
            rpcBeanDefination.setKlass(interfaces);
            rpcBeanDefination.setMethod(method);
            rpcBeanDefination.setObject(object);

            rpcBeanFactory.AddRpcBean(rpcBeanId, rpcBeanDefination);
        }
    }

通過傳過來的介面,得到這個介面中的所有方法,遍歷,把每個方法的資訊都注入到RpcBeanFactory的Map中。

2、RpcServer需要一直偵聽客戶機的請求,如果有請求,那麼RpcServerExecutor進行具體的處理。

分析:

@Override
    public void run() {
        try {
            // 接收Rpc客戶端傳遞的rpcBeanId和引數
            String rpcBeanId = ois.readUTF();
            Object[] parameters = (Object[]) ois.readObject();
            showParameters(parameters);
            // 定位相關類,物件和方法
            RpcBeanDefination rpcBeanDefination;
            rpcBeanDefination = rpcServer.getRpcBeanFactory().getRpcBean(rpcBeanId);
            // 執行Rpc客戶端要求執行的方法
            Method method = rpcBeanDefination.getMethod();
            Object object = rpcBeanDefination.getObject();
            Object result = method.invoke(object, parameters);
            // 向客戶端返回執行結果
            oos.writeObject(result);
        } catch (IOException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            closeSocket();
        }
    }

 這是RpcServerExecutor中的處理,接收->定位->執行->返回結果。

建立RPC客戶機端:

1、在代理物件的方法中,呼叫RpcClientExecutor。

/*jdk代理*/
    public Object getProxy(Class<?> klass) {
        // 判斷klass是否是介面,若不是,則應該採用cglib代理模式
        return Proxy.newProxyInstance(klass.getClassLoader(), new Class<?>[] { klass }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String rpcBeanId = String.valueOf(method.toString().hashCode());
                Object result = rpcClientExecutor.rpcExecutor(rpcBeanId, args);

                return result;
            }
        });
    }

 給方法生成雜湊值,呼叫rpcExecutor。

2、執行RpcClientExecutor。

<T> T rpcExecutor(String rpcBeanId, Object[] para) throws IOException, ClassNotFoundException {
        Socket socket = new Socket(rpcServerIp, rpcServerPort);
        ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
        // 給RPC伺服器傳送rpcBeanId和引數
        oos.writeUTF(rpcBeanId);
        oos.writeObject(para);
        // 接收RPC伺服器執行的結果,並返回
        ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
        Object result = ois.readObject();

        closeSocket(ois, oos, socket);

        return (T) result;
    }

客戶端處理很簡單:傳送資訊->接收資訊。