RPC的粗淺認識

再說RPC之前,先說一下同類的通信方式。
從單機走向分佈式,產生了很多分佈式的通信方式。

  •     最古老也是最有效,且永遠不過時的,TCP/UDP的二進制傳輸。事實上所有的通信方式歸根結底都是TCP/UDP。
  •     CORBA Common Object Request Broker Architecture。古老而複雜,支持面向對象的通信協議
  •     Web Service(SOA SOAP RDDI WSDL...協議):基於http + xml的標準化Web API。

        XML指的是接口文檔以XML的形式進行展現,http指的是通信協議。

        通信協議是http,以json方式進行數據傳出。

  •     RMI Remote Method Invocation:java內部的分佈式通信協議,就是Java內部封裝的RPC,不過名稱叫做RMI擺了。
  •     JMS Java Message Service:JavaEE中的消息框架標準,爲很多MQ所支持。
  •     RPC(Remote Procedure Call):遠程過程調用,就是調用遠程的方法。

遠程方法調用,這只是一個統稱,重點在於方法調用(不支持對象的概念),具體實現甚至可以用RMI RestFul等去實現,但一般不用,因爲RMI不能跨語言,而RestFul傳輸效率過低。

RPC多用於服務器集羣間的通信,因此常使用更加有效、短小精悍的傳出模式以提高效率,如Thrift,Hessian,gooogle protobuf。

RPC根據序列化方式的不同,出現了多樣的RPC序列化框架
    注意RPC序列化框架並不是RPC框架,RPC框架還包括服務發現,服務註冊、服務治理等。。。

  • java.io.Serializable

        java原生序列化,不好,序列化時間長,且序列化後的字節也非常大。

  • Hessian
  • google protobuf
  • facebook Thrift
  • kyro
  • fst
  • json序列化框架

        1 jackson

        2 google gson

        3 Ali FastJson

  • xmlrpc(stream)

RPC通信協議

  •     http
  •     http2.0(gRPC)
  •     TCP

        同步/異步/阻塞/非阻塞

  •     WebService

根據序列化方式和RPC通信協議方式的組合,可以產生多樣的RPC使用方式。

分佈式通信最基本最核心的部分:兩臺/多臺服務器之間的二進制數據的傳出。

以下是單機的二進制數據傳送與接收實例。

公共部分代碼:

public interface IUserService {

    public User findUserById(Integer id);
}


public class User implements Serializable {
    private static final long serialVersionUID = 1;

    private Integer id;
    private String name;

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

服務端代碼:


public class Server {
    private static boolean running = true;

    public static void main(String[] args) throws Exception{
        ServerSocket ss = new ServerSocket(8888);
        while(running){
            Socket s = ss.accept();
            process(s);
            s.close();
        }
        ss.close();
    }

    private static void process(Socket s)throws Exception{
        InputStream in = s.getInputStream();
        OutputStream out = s.getOutputStream();
        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);
        /*
        * 獲取客戶端的數據
        * */
        int id = dis.readInt();
        IUserService service = new UserServiceImpl();
        /*
        * 如何將對象寫入到客戶端呢?
        * 把對象的基本類型寫到客戶端。
        * */
        User user = service.findUserById(id);
        dos.writeInt(user.getId());
        dos.writeUTF(user.getName());
        dos.flush();
    }
}

User實現類

public class UserServiceImpl implements IUserService {
    @Override
    public User findUserById(Integer id) {
        return new User(id,"hello world");
    }
}

客戶端代碼:

/*
向外寫的過程:
客戶端的數據需要轉換成二進制的過程,
    通過DataOutputStream的寫入操作將123數值轉成二進制的表示,然後
    通過調用toByteArray方式轉成字節數組.
*/
public class client {
    public static void main(String[] args) throws Exception {
        Socket s = new Socket("127.0.0.1",8888);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        dos.writeInt(123);
        s.getOutputStream().write(baos.toByteArray());
        s.getOutputStream().flush();

        /*上方的操作進行寫出操作,下方讀取服務器返回的數據,然後
        * 將基本類型進行拼接成User對象*/
        DataInputStream dis = new DataInputStream(s.getInputStream());
        int id = dis.readInt();
        String name = dis.readUTF();
        User user = new User(id,name);

        System.out.println(user);

        dos.close();
        dis.close();
    }
}

視頻來源於馬士兵老師的公開課

筆記參考:Java講解RPC的基本實現

通過幾段 Java 代碼理解 RPC

這裏僅是粗淺理解性,對於RPC而言,其中動態代理扮演了比較重要的角色。

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