2012年12月26日

          簡單的遠程調用實現

          最近看到一個遠程調用的簡單實現,于是加上自己的理解分享給大家。
          遠程調用是典型的CS模型,Server端提供服務,客戶端調用得到結果

          先看服務端提供服務的方法
           1 /**
           2      * 提供服務
           3      *
           4      * @param service 服務實現
           5      * @param port    端口(可以雙發約定)
           6      * @throws Exception
           7      */
           8     public static void provide(final Object service, final int port) throws Exception {
           9         //參數檢查
          10         if (service == null) {
          11             throw new IllegalArgumentException("The service can't be null!");
          12         }
          13         if (port > 65535) {
          14             throw new IllegalArgumentException("The host can't greater than 65535!");
          15         }
          16         //開啟一個ServerSocket接收請求
          17         ServerSocket serverSocket = new ServerSocket(port);
          18         //死循環等待請求
          19         while (true) {
          20             //接受到請求,獲取socket
          21             final Socket socket = serverSocket.accept();
          22             try {
          23                 //開啟一個線程處理
          24                 new Thread(new Runnable() {
          25                     @Override
          26                     public void run() {
          27                         try {
          28                             try {
          29                                 //重socket中獲取輸入流
          30                                 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
          31                                 try {
          32                                     //獲取方法名
          33                                     String methodName = ois.readUTF();
          34                                     //獲取方法參數數組
          35                                     Class[] methodParameterTypes = (Class[]) ois.readObject();
          36                                     //獲取參數值數組
          37                                     Object[] arguments = (Object[]) ois.readObject();
          38                                     //根據方法名和方法參數獲取方法(根據方法名和方法參數可以唯一定位到一個方法)
          39                                     Method method = service.getClass().getMethod(methodName, methodParameterTypes);
          40                                     if (method == null) {
          41                                         throw new NoSuchMethodException();
          42                                     }
          43                                     //執行方法
          44                                     Object result = method.invoke(service, arguments);
          45                                     System.out.println("Method:" + methodName + ";Arguments:" + arguments + "  invoke!");
          46                                     //獲取socket輸出流
          47                                     ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
          48                                     try {
          49                                         //輸出結果
          50                                         oos.writeObject(result);
          51                                     } finally {
          52                                         oos.close();
          53                                     }
          54                                 } finally {
          55                                     ois.close();
          56                                 }
          57                             } finally {
          58                                 socket.close();
          59                             }
          60                         } catch (Exception e) {
          61                             //記個日志啥的
          62                             e.printStackTrace();
          63                         }
          64                     }
          65                 }).start();
          66             } catch (Exception e) {
          67                 //記個日志啥的
          68                 e.printStackTrace();
          69             }
          70         }
          71     }

          然后是消費的方法

          **
               * 消費服務
               *
               * @param clazz 接口類
               * @param host  發布服務機器的host
               * @param port  發布服務機器的port
               * @return
               */
              public static Object consume(final Class clazz, final String host, final int port) {
                  //參數檢查
                  if (clazz == null) {
                      throw new IllegalArgumentException("The clazz can't be null!");
                  }
                  if (host == null || host.isEmpty()) {
                      throw new IllegalArgumentException("The host can't be null or empty!");
                  }
                  if (port > 65535) {
                      throw new IllegalArgumentException("The host can't greater than 65535!");
                  }
                  //生成代理,每次調用方法其實是調用遠程的服務
                  Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
                          //建立socket鏈接
                          Socket socket = new Socket(host, port);
                          try {
                              //獲取輸出流
                              ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                              try {
                                  String methodName = method.getName();
                                  Class[] methodParameterTypes = method.getParameterTypes();
                                  //輸出要調用的方法名
                                  oos.writeUTF(methodName);
                                  //輸出要調用的方法參數列表
                                  oos.writeObject(methodParameterTypes);
                                  //輸出要調用的方法參數
                                  oos.writeObject(arguments);
                                  ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                                  try {
                                      //獲取結果
                                      Object result = ois.readObject();
                                      //可能返回的對象是異常
                                      if (result instanceof Throwable) {
                                          throw (Throwable) result;
                                      }
                                      return result;
                                  } finally {
                                      ois.close();
                                  }
                              } finally {
                                  oos.close();
                              }
                          } finally {
                              socket.close();
                          }
                      }
                  });
                  return proxy;
              }

          一般端口可以雙方約定,而host可以采用configServer的方法解決,也就是開啟一個服務,當啟動一個服務的時候到configServer注冊一下(服務名+host),如果是多臺服務器提供服務,host就是一個list,調用方發起調用的時候首先到configServer根據服務名獲取host列表,然后選一個host發起調用!configServer的優點是可以做到很多控制,比如流量控制,權重控制,調用host列表維護(死掉就剔除,重試機制)等等,這樣調用方不用關心我調用的是哪臺機器,只用關心我調用哪個方法。但也有壞處,一旦configServer掛掉了.......(其實也可以通過MS或調用方本地緩存調用列表解決)。

          一般由調用方提供一個接口包(算是一個雙方的約定),接口類中定義了提供發提供的方法
          如我們發布一個簡單的服務
          1 public interface Girl {
          2     //提供服務
          3     String server(String name);
          4 }

          實現
          1 public class GirlImpl implements Girl{
          2     @Override
          3     public String server(String name) {
          4         return name+"亞美爹";
          5     }
          6 }
          發布服務
          這里你可以寫在一個main方法中,也可以配置一個Spring的bean,并配置init方法,然后在init方法中開啟
          1  Girl beautifulGirl=new GirlImpl();
          2         try {
                          //在本機的1111端口上開啟Girl的服務
          3             Utils.provide(beautifulGirl, 1111);
          4         } catch (Exception e) {
          5             e.printStackTrace();
          6         }
          消費
          1  try {
          2             //從此你就獲得了一個漂亮妹子,她可以給你提供各種服務
          3             Girl beautifulGirl= (Girl)Utils.consume(Girl.class, "127.0.0.1", 3333);
          4             //你可以來一個循環,或者來一個死循環,一直哈哈
          5             beautifulGirl.server("yourName");
          6         } catch (Exception e) {
          7             e.printStackTrace();
          8         }

          總結:
          其實遠程調用也就是獲取服務的一個代理,每當你調用服務的方法事,他都會想服務方傳去方法,方法參數列表,參數,前兩個用于唯一確定一個方法,后一個用于方法調用。
          這里實現的很簡答,當然還有很復雜的,比如Spring的實現,淘寶的HSF等等


          以上為個人理解,如果有錯的地方,歡迎指正。

          posted @ 2012-12-26 19:25 Evan.lee 閱讀(1968) | 評論 (0)編輯 收藏

          <2012年12月>
          2526272829301
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          導航

          統計

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          最新評論

          主站蜘蛛池模板: 汉寿县| 江阴市| 兰溪市| 壤塘县| 鹿邑县| 西乌珠穆沁旗| 余姚市| 青岛市| 中江县| 科技| 新余市| 西昌市| 大关县| 江孜县| 山阳县| 玛曲县| 太原市| 洛浦县| 高碑店市| 康保县| 香河县| 呼伦贝尔市| 蕉岭县| 会泽县| 东乌| 青神县| 寿宁县| 综艺| 绿春县| 宁海县| 托克逊县| 哈巴河县| 定安县| 元朗区| 永福县| 习水县| 乐东| 栾城县| 垦利县| 中牟县| 自贡市|