rpc學習筆記

rpc

rpc 框架dubbo在使用時可以讓使用者調用遠程的接口的時候猶如調的本地接口,然而一個遠程過程調用一定是會使用網絡和序列化的,因此抽象一點,dubbo提供的核心的能力就是通過動態代理的方式把接口的網絡操作和序列化操作代理掉,對於使用者變成是透明的。dubbo在這個核心能力之上,外加了一些過濾,輪詢,服務註冊發現等功能,同時對網絡的操作和序列化的操作通過spi機制加入了很多動態的擴展,使得dubbo越來越龐大。因此理解dubbo,最好是從他核心去看,抽象出他的本質是要幹什麼再來看就會清楚一些。

基於java實現簡單的rpc

上面說了,dubbo抽象出核心的能力就是通過動態代理的方式把接口的網絡操作和序列化操作代理掉。之所以他很複雜,是因爲在這個核心之上加入了其他輔助功能。那麼通過一個動態代理就可以實現簡單的rpc

提供查詢當前時間服務的接口

package api;

/**
 * @Author: Jimmy
 * @Date: 2019-10-30 18:06
 */
public interface TimeService {

    public long getCurrentTimeMillis() throws Throwable;

    public Long getCurrentTimeSec() throws Throwable;

}

服務端timeService的具體實現

package provide;

import api.TimeService;

/**
 * @Author: Jimmy
 * @Date: 2019-10-30 18:08
 */
public class TimeServiceImpl implements TimeService {

    public long getCurrentTimeMillis()  {
        return System.currentTimeMillis();
    }

    public Long getCurrentTimeSec()  {
        return System.currentTimeMillis()/1000;
    }
}

服務端通過代理暴露服務(爲了簡單,這是用靜態代理,事實是使用動態代理讓使用者不知道這一層的代碼)

package provide;

import api.TimeService;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @Author: Jimmy
 * @Date: 2019-10-30 18:10
 */
public class TimeServiceProvideProxy extends Thread {

    //被代理的對象(具體的業務處理實現類)
    private TimeService timeService;

    public TimeServiceProvideProxy(TimeService timeService) {
        // 傳入代理對象(具體的業務處理實現類)
        this.timeService = timeService;
    }

    @Override
    public void run() {
        try {
            // 打開服務端Socket
            ServerSocket serverSocket = new ServerSocket(9000);
            // 接受客戶端的請求
            Socket socket = serverSocket.accept();
            while (socket != null) {
                //   ObjectInputStream是java 提供的序列化/反序列化的一種工具
                ObjectInputStream inStream =
                        new ObjectInputStream(socket.getInputStream());
                String method = (String) inStream.readObject();
                //根據客戶端傳入的請求 找到具體的方法,使用實現類去執行
                if (method.equals("getCurrentTimeMillis")) {
                    long currentTimeMillis = timeService.getCurrentTimeMillis();
                    ObjectOutputStream outStream =
                            new ObjectOutputStream(socket.getOutputStream());
                    // return result
                    outStream.writeLong(currentTimeMillis);
                    outStream.flush();
                }
                if (method.equals("getCurrentTimeSec")) {
                    long currentTimeSec = timeService.getCurrentTimeSec();
                    ObjectOutputStream outStream =
                            new ObjectOutputStream(socket.getOutputStream());
                    // 返回結果 通過  ObjectOutputStream序列化
                    outStream.writeLong(currentTimeSec);
                    outStream.flush();
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

客戶端發起請求的代理(爲了簡單,這是用靜態代理,事實是使用動態代理讓使用者不知道這一層的代碼)

package consumer;

import api.TimeService;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * @Author: Jimmy
 * @Date: 2019-10-30 18:14
 */
public class TimeServiceClientProxy implements TimeService {

    private Socket socket;

    public TimeServiceClientProxy() throws Throwable{
        // 客戶端的網絡鏈接
        socket = new Socket("127.0.0.1", 9000);
    }

    public long getCurrentTimeMillis() throws Throwable {
        // 沒有具體的業務,僅僅是封裝一個請求及協議(這裏的請求很簡單就是 一個方法名)
        // 服務端拿到這個請求 通過反序列化拿到結果,協議是定義好的就是一個簡單的方法名,服務端直接根據方法名去執行具體的方法
        ObjectOutputStream outStream =
                new ObjectOutputStream(socket.getOutputStream());
        outStream.writeObject("getCurrentTimeMillis");
        outStream.flush();
        ObjectInputStream inStream =
                new ObjectInputStream(socket.getInputStream());
        return inStream.readLong();
    }

    public Long getCurrentTimeSec() throws Throwable {
        ObjectOutputStream outStream =
                new ObjectOutputStream(socket.getOutputStream());
        outStream.writeObject("getCurrentTimeSec");
        outStream.flush();
        ObjectInputStream inStream =
                new ObjectInputStream(socket.getInputStream());
        return inStream.readLong();
    }

這樣一個最簡單的rpc調用就寫好了,客戶端是沒有具體的實現的,具體的實現在服務端。客戶端通過和服務端一起定義好的協議(方法名)通過一樣的序列化方式。完成遠程調用

啓動服務端

import provide.TimeServiceImpl;
import provide.TimeServiceProvideProxy;

/**
 * @Author: Jimmy
 * @Date: 2019-10-30 18:18
 */
public class ProvideMain {

    public static void main(String[] args) {
        TimeServiceImpl timeServiceImpl = new TimeServiceImpl();
        TimeServiceProvideProxy timeServiceProvideProxy = new TimeServiceProvideProxy(timeServiceImpl);
        timeServiceProvideProxy.start();
        System.out.println("服務端已經啓動。。。。。。");
    }
}

啓動客戶端

import consumer.TimeServiceClientProxy;

/**
 * @Author: Jimmy
 * @Date: 2019-10-30 18:20
 */
public class ConsumerMain {

    public static void main(String[] args) {
        try {
            TimeServiceClientProxy timeServiceClientProxy = new TimeServiceClientProxy();

            long millis = timeServiceClientProxy.getCurrentTimeMillis();
            System.out.println("millis:"+millis);
            Long sec = timeServiceClientProxy.getCurrentTimeSec();
            System.out.println("sec:"+sec);

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