簡單的遠程調(diào)用實現(xiàn)

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

          先看服務(wù)端提供服務(wù)的方法
           1 /**
           2      * 提供服務(wù)
           3      *
           4      * @param service 服務(wù)實現(xiàn)
           5      * @param port    端口(可以雙發(fā)約定)
           6      * @throws Exception
           7      */
           8     public static void provide(final Object service, final int port) throws Exception {
           9         //參數(shù)檢查
          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         //死循環(huán)等待請求
          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                                     //獲取方法參數(shù)數(shù)組
          35                                     Class[] methodParameterTypes = (Class[]) ois.readObject();
          36                                     //獲取參數(shù)值數(shù)組
          37                                     Object[] arguments = (Object[]) ois.readObject();
          38                                     //根據(jù)方法名和方法參數(shù)獲取方法(根據(jù)方法名和方法參數(shù)可以唯一定位到一個方法)
          39                                     Method method = service.getClass().getMethod(methodName, methodParameterTypes);
          40                                     if (method == null) {
          41                                         throw new NoSuchMethodException();
          42                                     }
          43                                     //執(zhí)行方法
          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                                         //輸出結(jié)果
          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     }

          然后是消費的方法

          **
               * 消費服務(wù)
               *
               * @param clazz 接口類
               * @param host  發(fā)布服務(wù)機器的host
               * @param port  發(fā)布服務(wù)機器的port
               * @return
               */
              public static Object consume(final Class clazz, final String host, final int port) {
                  //參數(shù)檢查
                  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!");
                  }
                  //生成代理,每次調(diào)用方法其實是調(diào)用遠程的服務(wù)
                  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();
                                  //輸出要調(diào)用的方法名
                                  oos.writeUTF(methodName);
                                  //輸出要調(diào)用的方法參數(shù)列表
                                  oos.writeObject(methodParameterTypes);
                                  //輸出要調(diào)用的方法參數(shù)
                                  oos.writeObject(arguments);
                                  ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                                  try {
                                      //獲取結(jié)果
                                      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的方法解決,也就是開啟一個服務(wù),當(dāng)啟動一個服務(wù)的時候到configServer注冊一下(服務(wù)名+host),如果是多臺服務(wù)器提供服務(wù),host就是一個list,調(diào)用方發(fā)起調(diào)用的時候首先到configServer根據(jù)服務(wù)名獲取host列表,然后選一個host發(fā)起調(diào)用!configServer的優(yōu)點是可以做到很多控制,比如流量控制,權(quán)重控制,調(diào)用host列表維護(死掉就剔除,重試機制)等等,這樣調(diào)用方不用關(guān)心我調(diào)用的是哪臺機器,只用關(guān)心我調(diào)用哪個方法。但也有壞處,一旦configServer掛掉了.......(其實也可以通過MS或調(diào)用方本地緩存調(diào)用列表解決)。

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

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

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


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

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


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          <2012年12月>
          2526272829301
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          最新評論

          主站蜘蛛池模板: 遂平县| 民权县| 栾川县| 搜索| 泽普县| 昭觉县| 乌兰浩特市| 曲水县| 宁城县| 横峰县| 乐陵市| 崇左市| 石泉县| 化州市| 普定县| 古浪县| 武威市| 青川县| 平舆县| 长春市| 司法| 炉霍县| 自治县| 滨海县| 临猗县| 新龙县| 方城县| 邯郸市| 渝中区| 外汇| 太康县| 杂多县| 石首市| 大理市| 新营市| 当涂县| 南城县| 黄石市| 库尔勒市| 滁州市| 兴安盟|