RPC, Remote Process Call, 遠程方法調用.java
RPC 將本地調用轉變爲遠程服務器上的調用,給系統的處理能力和吞吐量無限制提高提供了可能,是實現分佈式計算的基礎。json
擴容、服務隔離與分組 => 服務路由和負載均衡(router & load balance)瀏覽器
服務 consumer 經過 服務 provider的分組信息和地址信息進行路由,一般服務 provider 是一個集羣,須要採用必定負載均衡策略,選取其中一臺進行調用。服務器
序列化: 將對象轉化爲二進制流的過程。網絡
反序列化: 將二進制流轉化爲對象的過程。多線程
Java build-in、Google's Protocal Buffers、Hessian; JSON, XML。併發
能夠基於 Java 的 Socket API,實現一個簡單 RPC 調用.負載均衡
|- ISayHellosocket
|-- SayHelloService分佈式
|-- TCPRPCConsumer
|-- TCPRPCProvider
package com.land.rpc; public interface ISayHello { public String sayHello(String arg); }
package com.land.rpc; public class SayHelloService implements ISayHello { public String sayHello(String arg) { if ("hello".equals(arg)) { return "hello"; } return "bye"; } }
package com.land.rpc; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.net.Socket; public class TCPRPCConsumer { public static void main(String[] args) throws NoSuchMethodException, IOException, ClassNotFoundException { final String interfaceName = ISayHello.class.getName(); Method method = ISayHello.class.getMethod("sayHello", java.lang.String.class); Object[] methodArgs = {"hello"}; Socket socket = new Socket("127.0.0.1", 4080); // 對象傳輸協議 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeUTF(interfaceName); output.writeUTF(method.getName()); output.writeObject(method.getParameterTypes()); output.writeObject(methodArgs); ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); // 阻塞式 I/O String result = (String)input.readObject(); System.out.println(result); } }
package com.land.rpc; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; public class TCPRPCProvider { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Map<String, Object> services = new HashMap<String, Object>(); services.put("com.land.rpc.ISayHello", new SayHelloService()); final ServerSocket server = new ServerSocket(4080); while (true) { Socket socket = server.accept(); ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); String interaceName = input.readUTF(); String methodName = input.readUTF(); Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); Object[] methodArgs = (Object[]) input.readObject(); Object service = services.get(interaceName); Class<?> serviceInterfaceClass = Class.forName(interaceName); Method method = serviceInterfaceClass.getMethod(methodName, parameterTypes); Object result = method.invoke(service, methodArgs); ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeObject(result); } } }
若是使用 Socket API 來處理 HTTP 請求,一方面工做量比較大,效果還不必定好(底層的流處理,併發控制 etc)。 HttpClient 提供了一個成熟的解決方案。
###RPC: HTTP vs. TCP
Protocol | 優勢 | 缺點 |
---|---|---|
TCP | 處於協議棧的下層,能夠對協議字段進行定製,減小網絡開銷字節數,下降網絡開銷,提升性能,實現更大的吞吐量和併發數 | 實現成本高(須要考慮多線程併發、鎖、I/O etc 複雜的底層細節),難以跨平臺 |
HTTP | 不少成熟的容器已經很好處理, like. Tomcat, Apache, Jboss; 成熟的響應格式,like. JSON, XML | 處於協議棧的上層,同等內容信息比 TCP的字節數多,效率相對要低 => gzip 改善 |
兩種主流的 URL:
風格 | 示例 | 備註 |
---|---|---|
RPC | http://hostname/provider.do?service=com.http.sayhello&format=json×tamp=2016-09-05-20-36-00&arg1=arg1&arg2=arg2 | hostname/format/timestamp/args |
RESTful | POST http://hostname/people GET http://hostname/people/land PUT http://hostname/people/land DELETE http://hostname/people/land | RESTful = Representational State Transfer, 表現層狀態轉換. 利用了 HTTP 協議裏的幾種操做. POST: 建立,GET: 返回,PUT: 更新,DELETE: 刪除 |
變通 | POST arg1=hello arg2=123 URL http://hostname/provider/sayhelloservice/2016-09-05-20-36-00.json | provider=服務提供方;sayhelloservice=服務接口名稱;.json=服務端返回的數據格式;2016-09-05-20-36-00=客戶端訪問的時間戳 |
可使用 Servlet、Struts、Spring MVC 實現 HTTP RPC。