Java實現RPC(源碼)

一、RPC簡介

分佈式系統的成員之一,解決服務之間的調用問題。遠程調用時,RPC實現了調用遠程服務能夠像本地調用一樣方便,讓調用者感知不到遠程調用的邏輯。

二、RPC組成成員

  • 動態代理
  • 序列化
  • 網絡傳輸
  • 調用服務API接口
    本次RPC搭建使用的接口代理模式、Java默認Serializable序列化以及BIO網絡傳輸方式

三、源碼實現

  • 動態代理模塊封裝
package com.ruider.Invoker;

/**
 * 動態代理工廠,對相應的class生產其對應代理類
 */

import com.ruider.common.ServiceInformation;
import com.ruider.networkCommunication.BIOService;
import com.ruider.networkCommunication.NetIO;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class InvokerUtils implements InvocationHandler {

    /**
     * 調用服務類
     */
    private Class clazz;

    /**
     * 調用服務管理
     */
    private ServiceInformation serviceInformation;

    /**
     * 獲取代理對象
     * @param T
     * @param url
     * @param port
     * @return
     */
    public Object getBean (Class T, String url, int port) {
        this.clazz = T;
        ServiceInformation serviceInformation = new ServiceInformation();
        serviceInformation.setServiceURL(url);
        serviceInformation.setPort(port);
        serviceInformation.setClassName(T.getName());
        this.serviceInformation = serviceInformation;
        return Proxy.newProxyInstance(T.getClassLoader(), new Class[]{T}, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        NetIO BIO = new BIOService();
        this.serviceInformation.setMethod(method);
        this.serviceInformation.setArgs(args);
        return BIO.send(serviceInformation);
    }
}
  • 網絡傳輸模塊封裝
package com.ruider.networkCommunication;

import com.ruider.common.ServiceInformation;

/**
 * BIO服務
 */
public interface NetIO {

    /**
     * 客戶端發送請求到其他服務端
     * @param serviceInformation
     * @return
     */
    Object send (ServiceInformation serviceInformation);

    /**
     * 本機作爲服務端接受請求並且處理請求
     * @param port
     */
    void recv (int port);
}

package com.ruider.networkCommunication;

import com.ruider.common.ServiceInformation;
import com.ruider.common.UserApi;
import com.ruider.server.UserService;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 使用BIO通信
 */
public class BIOService implements NetIO {

    /**
     * 主機IP
     */
    private String url;
    /**
     * 端口
     */
    private int port;

    /**
     * 客戶端socket
     */
    private Socket clientSocket;

    /**
     * 服務端socket
     */
    private ServerSocket serverSocket;

    public BIOService() {}

    public BIOService(String url, int port) {
        this.url = url;
        this.port = port;
    }

    /**
     * 客戶端採用BIO通信方式發送請求信息
     * @param serviceInformation
     * @return
     */
    @Override
    public Object send(ServiceInformation serviceInformation) {

        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            this.clientSocket = new Socket(serviceInformation.getServiceURL(), serviceInformation.getPort());
            String className = serviceInformation.getClassName();
            String  methodName = serviceInformation.getMethod().getName();
            Class<?>[] parameterTypes = serviceInformation.getMethod().getParameterTypes();
            Object[] args = serviceInformation.getArgs();

            objectOutputStream = new ObjectOutputStream(this.clientSocket.getOutputStream());
            objectOutputStream.writeUTF(className);
            objectOutputStream.writeUTF(methodName);
            objectOutputStream.writeObject(parameterTypes);
            objectOutputStream.writeObject(args);

            objectInputStream = new ObjectInputStream(this.clientSocket.getInputStream());
            Object o = objectInputStream.readObject();
            System.out.println("Object = " + o);
            return o;
        }
        catch (Exception e) {
            if (this.clientSocket != null) {
                try {
                    this.clientSocket.close();
                }catch (IOException e1) {
                    e1.printStackTrace();
                }
            }

            if (objectOutputStream != null) {

                try {
                    objectOutputStream.close();
                }catch (IOException e1) {
                    e1.printStackTrace();
                }
            }

            if (objectInputStream != null) {
                try {
                    objectInputStream.close();
                }catch (IOException e1) {
                    e1.printStackTrace();
                }
            }

            return null;
        }
        finally {
            if (this.clientSocket != null) {
                try {
                    this.clientSocket.close();
                }catch (IOException e1) {
                    e1.printStackTrace();
                }
            }

            if (objectOutputStream != null) {

                try {
                    objectOutputStream.close();
                }catch (IOException e1) {
                    e1.printStackTrace();
                }
            }

            if (objectInputStream != null) {
                try {
                    objectInputStream.close();
                }catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }

    }

    /**
     * 服務端採用BIO通信獲取數據
     * @return
     */
    @Override
    public void recv (int port) {

        ObjectInputStream objectInputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
            this.serverSocket = new ServerSocket(port);
            System.out.println("啓動遠程服務監聽...");
            //監聽客戶端發來消息
            while (true){
                Socket socket = serverSocket.accept();
                objectInputStream = new ObjectInputStream(socket.getInputStream());

                //客戶端傳輸類名
                String className = objectInputStream.readUTF();
                String methodName = objectInputStream.readUTF();
                Class<?>[] parameterTypes = (Class<?>[])objectInputStream.readObject();
                Object[] arguments = (Object[]) objectInputStream.readObject();

                Class clazz = null;
                //服務匹配
                if(className.equals(UserApi.class.getName())){
                    clazz = UserService.class;
                }
                //clazz = UserService.class;

                Method method = clazz.getMethod(methodName,parameterTypes);
                Object result = method.invoke(clazz.newInstance(),arguments);

                objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                objectOutputStream.writeObject(result);
                objectOutputStream.flush();

                socket.close();
            }
        } catch (Exception e) {
            try {
                if (objectOutputStream != null) {
                    objectOutputStream.close();
                }
                if (objectInputStream != null) {
                    objectInputStream.close();
                }
                if (this.serverSocket != null) {
                    this.serverSocket.close();
                }
            }
            catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
        finally {
            try {
                if (objectOutputStream != null) {
                    objectOutputStream.close();
                }
                if (objectInputStream != null) {
                    objectInputStream.close();
                }
                if (this.serverSocket != null) {
                    this.serverSocket.close();
                }
            }
            catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}
  • 調用服務API接口
package com.ruider.common;

/**
 * RPC服務調用公用接口API
 */

public interface UserApi {

    User getUser(Integer id);
}

  • 調用服務信息管理類
package com.ruider.common;
/**
 * 被調用服務信息
 */

import java.io.Serializable;
import java.lang.reflect.Method;

public class ServiceInformation implements Serializable {

    /**
     * 被調用服務的主機ip
     */
    private String serviceURL;

    /**
     * 端口
     */
    private int port;

    /**
     * 類名
     */
    private String className;

    /**
     * 方法名
     */
    private Method method;

    /**
     * 方法參數
     */
    private Object[] args;

    public String getServiceURL() {
        return serviceURL;
    }

    public void setServiceURL(String serviceURL) {
        this.serviceURL = serviceURL;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Object[] getArgs() {
        return args;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }
}

四、功能測試

  • 客戶端
package com.ruider.client;

import com.ruider.RPC.RPC;
import com.ruider.common.User;
import com.ruider.common.UserApi;

public class Client {
    //本地調用遠程服務
    public  static  void  main(String args[]){
        UserApi userApi = (UserApi) RPC.rpc(UserApi.class, "127.0.0.1", 9000);
        User user = userApi.getUser(1);
        System.out.println("本地輸出遠程調用結果:" + user.toString());
    }
}
  • 服務端
package com.ruider.server;

import com.ruider.networkCommunication.BIOService;
import com.ruider.networkCommunication.NetIO;

public class Server {
    public static void main(String[] args) {
        NetIO BIOService = new BIOService();
        BIOService.recv(9000);
    }
}

package com.ruider.server;

import com.ruider.common.User;
import com.ruider.common.UserApi;

public class UserService implements UserApi {

    @Override
    public User getUser(Integer id) {
        User user=new User();
        user.setName("RuiDer");
        user.setId(id);
        System.out.println("user = " + user.toString());
        return user;
    }
}
  • 測試結果
    在這裏插入圖片描述
    在這裏插入圖片描述

五、GitHub源碼地址

https://github.com/RuiDer/RuiDer_RPC

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章