RPC基本原理及demo

一瞳筏、基本原理

1、RPC模型

流程:當 User 想發(fā)起一個遠程調用時慌盯,它實際是通過本地調用 User-stub周霉。 User-stub 負責將調用的接口、方法和參數(shù)通過約定的協(xié)議規(guī)范進行編碼并通過本地的 RPCRuntime 實例傳輸?shù)竭h端的實例亚皂。 遠端 RPCRuntime 實例收到請求后交給 Server-stub 進行解碼后發(fā)起向本地端 Server 的調用俱箱,調用結果再返回給 User 端。

2灭必、RPC組件細化

1狞谱、RpcServer:負責暴露(export)遠程接口
2乃摹、RpcClient:負責導入(import)遠程接口的代理實現(xiàn)
3、RpcProxy:遠程接口的代理實現(xiàn)
4跟衅、RpcInvoker:客戶端:負責編碼調用信息和發(fā)送調用請求到服務端并等待調用結果返回孵睬;服務端:負責調用服務端接口的具體實現(xiàn)并返回調用結果
5:、RpcProtocol:負責協(xié)議編/解碼
6伶跷、RpcConnector:負責維持客戶端和服務端的連接通道和發(fā)送數(shù)據(jù)到服務端
7掰读、RpcAcceptor:負責接收客戶端請求并返回請求結果
8、RpcProcessor:負責在服務端控制調用過程叭莫,包括管理調用線程池磷支、超時時間等
9、RpcChannel:數(shù)據(jù)傳輸通道

二食寡、RPC簡易實現(xiàn)

1、項目結構
2廓潜、公共服務接口
public interface HelloService {
    String sayHello(String name);
}
3抵皱、服務端實現(xiàn)

(1)實現(xiàn)類

public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return "hello " + name;
    }
}

(2)服務端Rpc服務類實現(xiàn)
其主要邏輯

1、注冊服務類
2辩蛋、啟動服務socket呻畸,循環(huán)等待客戶端連接
3、獲取客戶端傳送的信息
4悼院、根據(jù)信息解析并調用本地實現(xiàn)伤为,并回傳調用值

public class RpcServer {

    private static final HashMap<String, Class<?>> serviceRegistry = new HashMap<String, Class<?>>();
    private int port;

    public RpcServer(int port) {
        this.port = port;
    }

    public RpcServer register(Class serviceInterface, Class impClass) {
        //注冊服務(接口名:實現(xiàn)類名)
        serviceRegistry.put(serviceInterface.getName(), impClass);
        return this;
    }

    public void run() throws IOException {
        //1、啟動服務
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(port));
        System.out.println("start server据途!");
        ObjectInputStream input = null;
        ObjectOutputStream output = null;
        Socket socket = null;
        try {
            //2绞愚、循環(huán)等待連接
            for (; ; ) {
                //3、獲取客戶端的請求數(shù)據(jù)
                socket = serverSocket.accept();
                input = new ObjectInputStream(socket.getInputStream());
                String serviceName = input.readUTF();//客戶端傳過來的接口名
                String methodName = input.readUTF();//客戶端傳過來的方法名
                Class<?>[] parameterTypes = (Class<?>[]) input.readObject();//客戶端傳過來的方法參數(shù)類型
                Object[] args = (Object[]) input.readObject();//客戶端傳過來的參數(shù)值

                System.out.println("接受到客戶端的請求:serviceName:" + serviceName + ",methodName:" + methodName);
                //4颖医、根據(jù)客戶端傳過信息獲取注冊的服務端接口,并調用
                Class<?> serviceClass = serviceRegistry.get(serviceName);
                if (serviceClass == null) {
                    throw new ClassNotFoundException(serviceName + " not found!");
                }
                Method method = serviceClass.getMethod(methodName, parameterTypes);
                Object invokeResult = method.invoke(serviceClass.newInstance(), args);
                //5位衩、返回調用的方法
                output = new ObjectOutputStream(socket.getOutputStream());
                output.writeObject(invokeResult);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(input, output, socket);
        }

    }

    private void close(ObjectInputStream input, ObjectOutputStream output, Socket socket) {
        if (output != null) {
            try {
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (input != null) {
            try {
                input.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

(3)服務端啟動類

public class ServerMain {
    public static void main(String[] args) throws IOException {
        RpcServer rpcServer = new RpcServer(8888);
        rpcServer.register(HelloService.class, HelloServiceImpl.class);
        rpcServer.run();
    }
}
3、客戶端實現(xiàn)

主要邏輯

1熔萧、獲取ip和port糖驴,通過jdk代理遠程調用服務端
2、向服務端傳遞服務接口相關信息佛致,并獲取服務端返回的信息

(1)客戶端代理類實現(xiàn)

public class RpcClientProxy<T> implements InvocationHandler {
    /**
     * 服務端代理接口
     */
    private Class<T> serverInstance;
    /**
     * 服務端地址
     */
    private InetSocketAddress address;

    public RpcClientProxy(Class<T> serverInstance, String ip, Integer port) {
        this.address = new InetSocketAddress(ip, port);
        this.serverInstance = serverInstance;
    }

    /**
     * 獲取客戶端代理對象
     *
     * @return
     */
    public T getClientInstance() {
        return (T) Proxy.newProxyInstance(serverInstance.getClassLoader(), new Class<?>[]{serverInstance}, this);
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Socket socket = null;
        ObjectOutputStream outputStream = null;
        ObjectInputStream inputStream = null;
        try {
            // 1.創(chuàng)建Socket客戶端贮缕,根據(jù)指定地址連接遠程服務提供者
            socket = new Socket();
            socket.connect(address);
            //2、將遠程服務調用所需的接口類俺榆、方法名感昼、參數(shù)列表等編碼后發(fā)送給服務提供者
            outputStream = new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeUTF(serverInstance.getName());//接口名
            outputStream.writeUTF(method.getName());//方法名
            outputStream.writeObject(method.getParameterTypes());//方法參數(shù)類型,數(shù)組
            outputStream.writeObject(args);//具體方法參數(shù)值
            //3、同步阻塞等待服務器返回應答罐脊,獲取應答后返回
            inputStream = new ObjectInputStream(socket.getInputStream());
            return inputStream.readObject();
        } finally {
            if (socket != null) {
                socket.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

}

(2)客戶端啟動類

public class ClientMain {
    public static void main(String[] args) {
        RpcClientProxy clientProxy = new RpcClientProxy(HelloService.class, "127.0.0.1", 8888);
        HelloService helloService = (HelloService) clientProxy.getClientInstance();
        String result = helloService.sayHello("中國");
        System.out.println(result);
    }
}
4抑诸、流程總結
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末烂琴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蜕乡,更是在濱河造成了極大的恐慌奸绷,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件层玲,死亡現(xiàn)場離奇詭異号醉,居然都是意外死亡,警方通過查閱死者的電腦和手機辛块,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門畔派,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人润绵,你說我怎么就攤上這事线椰。” “怎么了尘盼?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵憨愉,是天一觀的道長。 經常有香客問我卿捎,道長配紫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任午阵,我火速辦了婚禮躺孝,結果婚禮上,老公的妹妹穿的比我還像新娘底桂。我一直安慰自己植袍,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布籽懦。 她就那樣靜靜地躺著奋单,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猫十。 梳的紋絲不亂的頭發(fā)上览濒,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音拖云,去河邊找鬼贷笛。 笑死,一個胖子當著我的面吹牛宙项,可吹牛的內容都是我干的乏苦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼汇荐,長吁一口氣:“原來是場噩夢啊……” “哼掀淘!你這毒婦竟也來了倾贰?” 一聲冷哼從身側響起匆浙,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤言秸,失蹤者是張志新(化名)和其女友劉穎井仰,沒想到半個月后破加,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年玫锋,在試婚紗的時候發(fā)現(xiàn)自己被綠了撩鹿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片键思。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖失乾,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情纽竣,我是刑警寧澤茧泪,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布穴吹,位于F島的核電站港令,受9級特大地震影響顷霹,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一革屠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧似芝,春花似錦那婉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至寞奸,卻和暖如春呛谜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枪萄。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工隐岛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瓷翻。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓聚凹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親齐帚。 傳聞我的和親對象是個殘疾皇子妒牙,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容