手寫一個簡單的RPC框架

? ? ? ? 隨著soa的出現(xiàn)和普及,遠程分布式調用已經成為主流仗岸。逐漸流行起來的微服務更是讓RPC大放異彩耿焊。目前業(yè)界開源的RPC框架非常多,比較主流的RPC框架有facebook開發(fā)的Apache Thrift奕坟,Hadoop的Avro-RPC祥款,google開源的gRpc等。dubbo也是基于RPC框架之上的一個服務治理框架月杉。

? ? ? ? 下面將通過java原生的序列化刃跛,Socket通信,動態(tài)代理和反射機制苛萎,實現(xiàn)一個簡單的RPC框架桨昙。

????????它主要由三部分組成:

1检号、服務提供者:運行在服務端,負責提供服務接口定義和服務實現(xiàn)類蛙酪。

2齐苛、服務發(fā)布者:它運行在RPC服務端,負責將本地服務發(fā)布成遠程服務滤否,供其他消費者調用脸狸。

3、本地服務代理:它運行在RPC客戶端藐俺,通過代理調用遠程服務提供者炊甲,然后將結果進行封裝返回給本地消費者。

????????首先是服務端接口定義和實現(xiàn):

public interface EchoService {

????????String echo(String ping);

}

public class EchoServiceImpl implements EchoService{

????@Override

????public String echo(String ping) {

????????return ping!=null?ping +" I am Ok":" I am Ok";

????}

}

服務端的服務發(fā)布者代碼如下:

public class RpcExporter {

????????static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

????????public static void exporter(String hostName , int port) throws Exception{

????????????????ServerSocket server = new ServerSocket();

????????????????server.bind(new InetSocketAddress(hostName, port));

????????????????try{

????????????????????????while(true){

????????????????????????????executor.execute(new ExporterTask(server.accept()));

????????????????????????}

????????????????}catch(Exception e){

????????????????????e.printStackTrace();

????????????????}

????????}

????????private static class ExporterTask implements Runnable{

????????????Socket client = null ;

????????????public ExporterTask(Socket client){

????????????this.client = client;

????????????}

????????????@Override

????????????public void run(){

????????????????????ObjectInputStream input = null ;

????????????????????ObjectOutputStream out = null;

????????????????try{

????????????????????//從client獲取數(shù)據(jù)

????????????????????input = new ObjectInputStream(client.getInputStream());

????????????????????String interfaceName = input.readUTF();

????????????????????Class service = Class.forName(interfaceName);

????????????????????String methodName = input.readUTF();

????????????????????Class[] parameterTypes = (Class[])input.readObject();

????????????????????Object[] arguments = (Object[] )input.readObject();

????????????????????Method method = service.getMethod(methodName, parameterTypes);

????????????????????Object result = method.invoke(service.newInstance(), arguments);

????????????????????out = new ObjectOutputStream(client.getOutputStream());

????????????????????out.writeObject(result);

????????????????}catch(Exception e){

????????????????????e.printStackTrace();

????????????????}finally{

????????????????????????try {

????????????????????????????input.close();

????????????????????????????out.close();

????????????????????????} catch (IOException e) {

????????????????????????????// TODO Auto-generated catch block

????????????????????????????e.printStackTrace();

????????????????????????}

????????????}

????????}

????}

}

????????服務發(fā)布者的主要職責如下:

(1)作為服務端欲芹,監(jiān)聽客戶端的TCP連接卿啡,接收新的客戶端連接之后,將其封裝成Task菱父,讓線程池執(zhí)行颈娜。

(2)將客戶端發(fā)送的碼流反序列化成對象,反射調用服務實現(xiàn)者浙宜,獲取執(zhí)行結果官辽。

(3)將執(zhí)行結果對象反序列化,通過Socket發(fā)送給客戶端粟瞬。

(4)遠程服務調用完成之后同仆,釋放socket等連接資源,防止句柄泄露裙品。

????????接下來是客戶端本地服務代理源碼:

public class RpcImporter {

????????@SuppressWarnings("unchecked")

????????public S importer(final Class serviceClass , final InetSocketAddress addr){

????????????return (S)Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class[]{

????????????serviceClass.getInterfaces()[0]}, new InvocationHandler() {

????????????????????Socket socket = null ;

????????????????????ObjectOutputStream output = null ;

????????????????????ObjectInputStream input = null ;

????????????????????@Override

????????????????????public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

? ? ? ? ? ? ? ? ? ? ? ? ?try{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? socket = new Socket();

????????????????????????????????socket.connect(addr);

????????????????????????????????output = new ObjectOutputStream(socket.getOutputStream());

????????????????????????????????output.writeUTF(serviceClass.getName());

????????????????????????????????output.writeUTF(method.getName());

????????????????????????????????output.writeObject(method.getParameterTypes());

????????????????????????????????output.writeObject(args);

????????????????????????????????input = new ObjectInputStream(socket.getInputStream());

????????????????????????????????return input.readObject();

????????????????????????}finally{

????????????????????????????if(output!=null){

? ? ? ? ? ? ? ? ? ? ? ? ????? ? output.close();

? ? ? ? ? ? ? ? ? ? ? ? ? ? }

????????????????????????????if(input!=null){

????????????????????????????????input.close();

? ? ? ? ? ? ? ? ? ? ? ? ? ? }

????????????????????}

????????????????}

? ? ? ? ?});

? ? ?}

}

????????本地服務代理的主要功能如下:

(1)將本地的接口調用轉化為JDK的動態(tài)代理俗批,在動態(tài)代理中實現(xiàn)接口的遠程調用。

(2)創(chuàng)建Socket客戶端市怎,根據(jù)指定地址連接遠程服務提供者岁忘。

(3)同步阻塞等待服務端返回應答,回去應答之后返回区匠。

????????下面是測試代碼:

public class RpcTest {

????????public static void main(String[] args){

????????????//啟動服務端 輪詢接受客戶端的請求

????????????new Thread(new Runnable(){

????????????????@Override

????????????????public void run() {

????????????????????try{

????????????????????????RpcExporter.exporter("127.0.0.1", 8080);

? ? ? ? ? ? ? ? ? ? }catch(Exception e){

????????????????????????e.printStackTrace();

????????????????????}

? ? ? ? ? ? ?}

????????????}).start();

????????????RpcImporter importer = new RpcImporter();

????????????EchoService echo = importer.importer(EchoServiceImpl.class, new InetSocketAddress("localhost",8080));

????????????System.out.println(echo.echo("are you ok ? "));

????????}

}

? ? ? ? 首先干像,創(chuàng)建一個異步發(fā)布服務端的線程并啟動,用于接收RPC客戶端的請求驰弄,根據(jù)請求參數(shù)調用服務實現(xiàn)類麻汰,返回結果給客戶端。隨后揩懒,創(chuàng)建客戶端服務代理類什乙,構造RPC請求參數(shù),發(fā)起RPC調用已球,將調用結果輸出到控制臺臣镣。執(zhí)行結果如下所示:

????????are you ok ? I am Ok

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末辅愿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子忆某,更是在濱河造成了極大的恐慌点待,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弃舒,死亡現(xiàn)場離奇詭異癞埠,居然都是意外死亡,警方通過查閱死者的電腦和手機聋呢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門苗踪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人削锰,你說我怎么就攤上這事通铲。” “怎么了器贩?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵颅夺,是天一觀的道長。 經常有香客問我蛹稍,道長吧黄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任唆姐,我火速辦了婚禮拗慨,結果婚禮上,老公的妹妹穿的比我還像新娘厦酬。我一直安慰自己胆描,他們只是感情好瘫想,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布仗阅。 她就那樣靜靜地躺著,像睡著了一般国夜。 火紅的嫁衣襯著肌膚如雪减噪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天车吹,我揣著相機與錄音筹裕,去河邊找鬼。 笑死窄驹,一個胖子當著我的面吹牛朝卒,可吹牛的內容都是我干的。 我是一名探鬼主播乐埠,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抗斤,長吁一口氣:“原來是場噩夢啊……” “哼囚企!你這毒婦竟也來了?” 一聲冷哼從身側響起瑞眼,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤龙宏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伤疙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體银酗,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年徒像,在試婚紗的時候發(fā)現(xiàn)自己被綠了捶牢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碴倾。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出垮衷,到底是詐尸還是另有隱情,我是刑警寧澤吵聪,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布硫嘶,位于F島的核電站,受9級特大地震影響拭抬,放射性物質發(fā)生泄漏部默。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一造虎、第九天 我趴在偏房一處隱蔽的房頂上張望傅蹂。 院中可真熱鬧,春花似錦算凿、人聲如沸份蝴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婚夫。三九已至,卻和暖如春署鸡,著一層夾襖步出監(jiān)牢的瞬間案糙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工靴庆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留时捌,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓炉抒,卻偏偏與公主長得像奢讨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子焰薄,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容