從頭創(chuàng)建一個簡單的RPC服務(wù)框架

  • 概念解釋

    RPC(Remote Procedure Call Protocol)——遠(yuǎn)程過程調(diào)用協(xié)議秩仆,它是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請求服務(wù)贮匕,而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議冈爹。RPC采用客戶機(jī)/服務(wù)器模式。請求程序就是一個客戶機(jī)奋构,而服務(wù)提供程序就是一個服務(wù)器手趣。首先晌该,客戶機(jī)調(diào)用進(jìn)程發(fā)送一個有進(jìn)程參數(shù)的調(diào)用信息到服務(wù)進(jìn)程,然后等待應(yīng)答信息绿渣。在服務(wù)器端朝群,進(jìn)程保持睡眠狀態(tài)直到調(diào)用信息到達(dá)為止。當(dāng)一個調(diào)用信息到達(dá)中符,服務(wù)器獲得進(jìn)程參數(shù)姜胖,計(jì)算結(jié)果,發(fā)送答復(fù)信息淀散,然后等待下一個調(diào)用信息右莱,最后,客戶端調(diào)用進(jìn)程接收答復(fù)信息档插,獲得進(jìn)程結(jié)果慢蜓,然后調(diào)用執(zhí)行繼續(xù)進(jìn)行。

  • ServerSocket 與 Socket

    • 客戶端與服務(wù)端建立連接基本原理


      http://hi.csdn.net/attachment/201009/12/0_1284280120c10H.gif
    • 客戶端與服務(wù)端建立連接流程


      http://hi.csdn.net/attachment/201009/12/0_1284280127Kfu0.gif
    • ServerSocket

      ServerSocket server = new ServerSocket(1234);  
      Socket socket = server.accept(); 
      

      創(chuàng)建并監(jiān)聽一個端口為1234的ServerSocket對象郭膛,然后調(diào)用了ServerSocket對象的accept()方法晨抡,這個方法的執(zhí)行將使Server端的程序處于阻塞狀態(tài),程序?qū)⒁恢弊枞钡讲蹲降揭粋€來自Client端的請求则剃,并返回一個用于與該Client通信的Socket對象耘柱。此后Server程序只要向這個Socket對象讀寫數(shù)據(jù),就可以實(shí)現(xiàn)向遠(yuǎn)端的Client讀寫數(shù)據(jù)棍现。結(jié)束監(jiān)聽時调煎,關(guān)閉ServerSocket對象

    • Socket

      Socket socket = new Socket("127.0.0.1", 1234);
      

      客戶端創(chuàng)建一個socket對象,與服務(wù)端建立連接轴咱,服務(wù)端管理客戶連接請求的任務(wù)由操作系統(tǒng)完成汛蝙,操作系統(tǒng)把連接請求存儲在一個先進(jìn)先出的隊(duì)列中,隊(duì)列長度一般為50朴肺。當(dāng)服務(wù)端連接隊(duì)列長度大于50時窖剑,連接被拒絕。

  • 動態(tài)代理機(jī)制
    java的動態(tài)代理機(jī)制中戈稿,有兩個重要的類和接口西土,Proxy、InvocationHandler鞍盗,他們都在java.lang.reflect包下需了,這個類和接口是在實(shí)現(xiàn)動態(tài)代理過程中必須用到的

    • Proxy
      Proxy用于創(chuàng)建一個代理類對象跳昼,它提供了許多方法,其中應(yīng)用最多的是 newProxyInstance這個方法肋乍。

      public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
      

      參數(shù)解釋:
      loader: 一個ClassLoader對象鹅颊,定義了代理對象是由哪個Classloader對象來加載
      interfaces: 一個interface數(shù)組,表示代理我們將為代理對象提供一組什么接口墓造,如果我提供了一組接口堪伍,則代理對象就宣稱實(shí)現(xiàn)了該組接口,這樣我們就能通過代理對象調(diào)用相應(yīng)接口了
      h: 一個InvocationHandler對象觅闽,表示動態(tài)代理對象在調(diào)用方法時具體執(zhí)行的對象

    • InvocationHandler
      每個動態(tài)代理類都必須實(shí)現(xiàn)InvocationHandler接口帝雇,且每個代理類的實(shí)例都關(guān)聯(lián)到一個InvocationHandler對象,當(dāng)代理對象調(diào)用一個方法時蛉拙,這個方法的調(diào)用會映射到InvocationHandler的invoke方法執(zhí)行

      public Object invoke(Object proxy, Method method, Object[] args)
      

      參數(shù)解釋:
      proxy: 指代代理的真實(shí)對象
      method: 指代所要調(diào)用的真實(shí)對象的某個方法
      args: 指代調(diào)用真實(shí)對象方法時接受的參數(shù)

    • 動態(tài)代理示例

      • 定義測試接口

        public interface TestHello {
        
            void hello(String name);
            
            void sayGood();
        }
        
      • 定義接口實(shí)現(xiàn)

        public class TestHelloImpl implements TestHello{
        
            @Override
            public void hello(String name) {
                System.out.println("hello " + name);
            }
        
            @Override
            public void sayGood() {
                System.out.println("you are the best!");
            }
        }
        
      • 定義動態(tài)代理類

        public class MyProxy implements InvocationHandler {
        
            private Object proxy;
            
            public MyProxy(Object proxy) {
                this.proxy = proxy;
            }
        
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                method.invoke(this.proxy, args);
                return null;
            }
        
        }
        
      • 模擬代理調(diào)用

        public class Client {
        
            public static void main(String[] args) {
        
                TestHello th = new TestHelloImpl();
        
                MyProxy mp = new MyProxy(th);
        
                TestHello thProxy =
                    (TestHello) Proxy.newProxyInstance(mp.getClass().getClassLoader(), th.getClass().getInterfaces(), mp);
        
                thProxy.hello("Jerry");
        
                thProxy.sayGood();
            }
        }
        
      • 測試輸出

        hello Jerry
        you are the best!
        
  • 一個簡單的RPC實(shí)現(xiàn)

    • 簡單RPC框架類尸闸,定義服務(wù)注冊方法 export, 服務(wù)消費(fèi)方法 refer

      public class RpcFramework {
      
          /** 
           * 暴露服務(wù) 
           *  
           * @param service 服務(wù)實(shí)現(xiàn) 
           * @param port 服務(wù)端口 
           * @throws Exception 
           */  
          public static void export(final Object service, int port) throws Exception {  
              if (service == null)  
                  throw new IllegalArgumentException("service instance == null");  
              if (port <= 0 || port > 65535)  
                  throw new IllegalArgumentException("Invalid port " + port);  
              System.out.println("Export service " + service.getClass().getName() + " on port " + port);  
              ServerSocket server = new ServerSocket(port);  
              for(;;) {  
                  try {  
                      final Socket socket = server.accept();  
                      new Thread(new Runnable() {  
                          @Override  
                          public void run() { 
                              try {  
                                  try {  
                                      ObjectInputStream input = new ObjectInputStream(socket.getInputStream());  
                                      try {  
                                          String methodName = input.readUTF(); 
                                          Class<?>[] parameterTypes = (Class<?>[])input.readObject();  
                                          Object[] arguments = (Object[])input.readObject();  
                                          ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());  
                                          try {  
                                              Method method = service.getClass().getMethod(methodName, parameterTypes);  
                                              Object result = method.invoke(service, arguments);  
                                              output.writeObject(result);
                                          } catch (Throwable t) {  
                                              output.writeObject(t);  
                                          } finally {  
                                              output.close();  
                                          }  
                                      } finally {  
                                          input.close();  
                                      }  
                                  } finally {  
                                      socket.close();  
                                  }
                              } catch (Exception e) {  
                                  e.printStackTrace();  
                              }
                          }  
                      }).start();  
                  } catch (Exception e) {  
                      e.printStackTrace();  
                  }
              }
          }  
        
          /** 
           * 引用服務(wù) 
           *  
           * @param <T> 接口泛型 
           * @param interfaceClass 接口類型 
           * @param host 服務(wù)器主機(jī)名 
           * @param port 服務(wù)器端口 
           * @return 遠(yuǎn)程服務(wù) 
           * @throws Exception 
           */  
          @SuppressWarnings("unchecked")  
          public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {  
              if (interfaceClass == null)  
                  throw new IllegalArgumentException("Interface class == null");  
              if (! interfaceClass.isInterface())  
                  throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");  
              if (host == null || host.length() == 0)  
                  throw new IllegalArgumentException("Host == null!");  
              if (port <= 0 || port > 65535)  
                  throw new IllegalArgumentException("Invalid port " + port);  
              System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);  
              return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {  
                  public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {  
                      Socket socket = new Socket(host, port);  
                      try {  
                          ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());  
                          try {  
                              output.writeUTF(method.getName());  
                              output.writeObject(method.getParameterTypes());  
                              output.writeObject(arguments);  
                              InputStream is = socket.getInputStream();
                              ObjectInputStream input = new ObjectInputStream(is);  
                              try {  
                                  Object result = input.readObject();  
                                  if (result instanceof Throwable) {  
                                      throw (Throwable) result;  
                                  }  
                                  return result;  
                              } finally {  
                                  input.close();  
                              }  
                          } finally {  
                              output.close();  
                          }  
                      } finally {  
                          socket.close();  
                      }  
                  }  
              });  
        
      }  
          
      }
      
    • 定義具體接口和實(shí)現(xiàn)

      public interface HelloService {
      
          String hello(String name);  
      
      }
      
      public class HelloServiceImpl implements HelloService{
      
          @Override
          public String hello(String name) {
              return "hello " + name;
          }
      
      }
      
    • 生成并注冊服務(wù)對象到服務(wù)端

      public class RpcProvider {
      
          public static void main(String[] args) {
              try {
                  HelloService service = new HelloServiceImpl();  
                  RpcFramework.export(service, 1235);            
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      
    • 客戶端聲明服務(wù)端定義的服務(wù)對象,使用服務(wù)

      public class RpcConsumer {
      
          public static void main(String[] args) {
              try {
                  HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1235);  
                  for (int i = 0; i < Integer.MAX_VALUE; i ++) {  
                      String hello = service.hello("World" + i);  
                      System.out.println(hello);  
                      Thread.sleep(1000);  
                  }              
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      
    • 本文多數(shù)內(nèi)容非原創(chuàng)孕锄,僅供學(xué)習(xí)使用吮廉,歡迎指正!
  • 參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末硫惕,一起剝皮案震驚了整個濱河市茧痕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恼除,老刑警劉巖踪旷,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異豁辉,居然都是意外死亡令野,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門徽级,熙熙樓的掌柜王于貴愁眉苦臉地迎上來气破,“玉大人,你說我怎么就攤上這事餐抢∠质梗” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵旷痕,是天一觀的道長碳锈。 經(jīng)常有香客問我,道長欺抗,這世上最難降的妖魔是什么售碳? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上贸人,老公的妹妹穿的比我還像新娘间景。我一直安慰自己,他們只是感情好艺智,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布倘要。 她就那樣靜靜地躺著,像睡著了一般力惯。 火紅的嫁衣襯著肌膚如雪碗誉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天父晶,我揣著相機(jī)與錄音,去河邊找鬼弄跌。 笑死甲喝,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的铛只。 我是一名探鬼主播埠胖,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淳玩!你這毒婦竟也來了直撤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蜕着,失蹤者是張志新(化名)和其女友劉穎谋竖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體承匣,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蓖乘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了韧骗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘉抒。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖袍暴,靈堂內(nèi)的尸體忽然破棺而出些侍,到底是詐尸還是另有隱情,我是刑警寧澤岗宣,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站览徒,受9級特大地震影響狈定,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纽什,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望企巢。 院中可真熱鬧,春花似錦探孝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庇配。三九已至,卻和暖如春绍些,著一層夾襖步出監(jiān)牢的瞬間捞慌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工柬批, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啸澡,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓萝快,卻偏偏與公主長得像锻霎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子揪漩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理旋恼,服務(wù)發(fā)現(xiàn),斷路器奄容,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • 從三月份找實(shí)習(xí)到現(xiàn)在冰更,面了一些公司,掛了不少昂勒,但最終還是拿到小米蜀细、百度、阿里戈盈、京東奠衔、新浪谆刨、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,209評論 11 349
  • 多態(tài) 任何域的訪問操作都將有編譯器解析归斤,如果某個方法是靜態(tài)的痊夭,它的行為就不具有多態(tài)性 java默認(rèn)對象的銷毀順序與...
    yueyue_projects閱讀 936評論 0 1
  • 進(jìn)程和線程 什么是進(jìn)程(process)? An executing instance of a program ...
    狗狗胖妞閱讀 303評論 0 0
  • 1澳洲龍蝦880脏里,羊排98她我,土豆絲18,你會選擇哪一個中間的吧迫横,實(shí)際上第一個都沒有在一個中檔的餐廳 2 38(普通...
    bad_boy閱讀 313評論 2 0