2012年12月26日

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

          最近看到一個遠(yuǎn)程調(diào)用的簡單實現(xiàn),于是加上自己的理解分享給大家。
          遠(yuǎ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)用遠(yuǎn)程的服務(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列表維護(hù)(死掉就剔除,重試機制)等等,這樣調(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é):
          其實遠(yuǎn)程調(diào)用也就是獲取服務(wù)的一個代理,每當(dāng)你調(diào)用服務(wù)的方法事,他都會想服務(wù)方傳去方法,方法參數(shù)列表,參數(shù),前兩個用于唯一確定一個方法,后一個用于方法調(diào)用。
          這里實現(xiàn)的很簡答,當(dāng)然還有很復(fù)雜的,比如Spring的實現(xiàn),淘寶的HSF等等


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

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

          僅列出標(biāo)題  
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          最新評論

          主站蜘蛛池模板: 济阳县| 昭平县| 宝坻区| 泗阳县| 宣化县| 新宾| 南昌市| 中卫市| 应城市| 葫芦岛市| 洪雅县| 临夏市| 霸州市| 竹北市| 图片| 盐山县| 黔东| 墨脱县| 四会市| 宁海县| 清水河县| 阿尔山市| 曲麻莱县| 汤阴县| 外汇| 奉节县| 通榆县| 奇台县| 红安县| 新乐市| 富阳市| 罗甸县| 沁源县| 雅江县| 海兴县| 陈巴尔虎旗| 吉木萨尔县| 东丰县| 合肥市| 郑州市| 上高县|