RPC(Remote Promote Call) 一種進程間通訊方式。容許像調用本地服務同樣調用遠程服務。java
RPC框架的主要目標就是讓遠程服務調用更簡單、透明。RPC框架負責屏蔽底層的傳輸方式(TCP或者UDP)、序列化方式(XML/JSON/二進制)和通訊細節。開發人員在使用的時候只須要了解誰在什麼位置提供了什麼樣的遠程服務接口便可,並不須要關心底層通訊細節和調用過程。node
RPC框架原理圖web
簡單的RPC代碼實現服務器
package Server; public interface EchoService { String echo(String ping); } package Server; public class EchoServiceImpl implements EchoService{ @Override public String echo(String ping) { // TODO Auto-generated method stub return ping !=null?ping+"--> I am ok.":"I am bad."; } } package Exporter; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * RPC服務端發佈者 * 做爲服務端,監聽客戶端的TCP鏈接,接收到新的客戶端鏈接以後,將其封裝成Task,由線程池執行 * 將客戶端發送的碼流反序列化成對象,反射調用服務實現者,獲取執行結果 * 將執行結果對象發序列化,經過Socket發送給客戶端 * 遠程調用完成以後,釋放Socket等鏈接資源,防止句柄泄露 * @author Administrator * */ public class RpcExporter { //建立一個可重用固定線程數的線程池 //Runtime.getRuntime().availableProcessors()返回虛擬機可用的處理器數量 static Executor executor=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public static void exporter(String hostname,int port) throws IOException { //建立一個監聽特定端口的Serversocket,負責接收客戶鏈接請求 ServerSocket server = new ServerSocket(); //綁定主機名端口號 server.bind(new InetSocketAddress(hostname,port)); try{ while(true) { executor.execute(new ExporterTask(server.accept())); } }finally { server.close(); } } private static class ExporterTask implements Runnable{ Socket client=null; public ExporterTask(Socket client){ this.client=client; } @Override public void run() { // TODO Auto-generated method stub ObjectInputStream input=null; ObjectOutputStream output=null; try{ //獲取輸入流 input=new ObjectInputStream(client.getInputStream()); //獲取調用的接口名 String interfaceName = input.readUTF(); //加載接口 Class<?> service = Class.forName(interfaceName); //獲取調用的方法名 String methodName = input.readUTF(); //獲取方法返回類型 Class<?>[] ParameterTypes = (Class<?>[]) input.readObject(); //獲取參數 Object[] arguments = (Object[]) input.readObject(); //經過反射獲取方法 Method method = service.getMethod(methodName, ParameterTypes); //經過反射調用方法 Object result = method.invoke(service.newInstance(), arguments); output = new ObjectOutputStream(client.getOutputStream()); output.writeObject(result); }catch(Exception e){ e.printStackTrace(); } finally{ if(output != null) try{ output.close(); }catch ( IOException e){ e.printStackTrace(); } if(input !=null) try{ input.close(); }catch(IOException e){ e.printStackTrace(); } if(client != null) try{ client.close(); }catch (IOException e){ e.printStackTrace(); } } } } } package Client; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.Socket; /** *本地服務代理 *將本地的接口調用轉換成JDK的動態代理,在動態代理中實現接口的遠程調用 *建立Socket客戶端,根據指定地址鏈接遠程服務提供者 *將遠程服務調用所須要的接口類,方法名,參數列表等編碼參數發送給服務提供者 *同步阻塞等待服務端返回應答,獲取應答以後返回 * @author Administrator * * @param <S> */ public class RpcImporter<S> { @SuppressWarnings("unchecked") public S importer(final Class<?> serviceClass,final InetSocketAddress addr) { return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[] {serviceClass.getInterfaces()[0]}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub Socket socket =null; ObjectOutputStream output = null; ObjectInputStream input = null; try{ socket = new Socket(); socket.connect(addr); //將遠程服務調用所須要的接口類,方法名,參數列表等編碼參數發送給服務提供者 output = new ObjectOutputStream(socket.getOutputStream()); output.writeUTF(serviceClass.getName()); output.writeUTF(method.getName()); output.writeObject(method.getParameterTypes()); output.writeObject(args); //同步阻塞等待服務端返回應答,獲取應答以後返回 input= new ObjectInputStream(socket.getInputStream()); return input.readObject(); } finally{ if(socket != null) socket.close(); if(output != null) output.close(); if(input != null) input.close(); } } }); } } package test; import java.net.InetSocketAddress; import Client.RpcImporter; import Exporter.RpcExporter; import Server.EchoService; import Server.EchoServiceImpl; public class run { public static void main(String[] args) { // TODO Auto-generated method stub //建立異步發佈服務端的線程並啓動,用於接受PRC客戶端的請求,根據請求參數調用服務實現類,返回結果給客戶端 new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub try{ RpcExporter.exporter("localhost", 8088); }catch (Exception e){ e.printStackTrace(); } } }).start(); //建立客戶端服務代理類,構造RPC求情參數,發起RPC調用 RpcImporter<EchoService> importer=new RpcImporter<EchoService>(); EchoService echo = importer.importer(EchoServiceImpl.class, new InetSocketAddress("localhost",8088)); System.out.println(echo.echo("Are u ok?")); } }
RPC框架實現的幾個核心技術點:數據結構
(1)遠程提供者須要以某種形式提供服務調用相關的信息,包括但不限於服務接口定義、數據結構、或者中間態的服務定義文件。例如Facebook的 Thrift的IDL文件,Web service的WSDL文件;服務的調用者須要經過必定的圖景獲取遠程服務調用相關的信息。架構
(2)遠程代理對象:服務調用者用的服務實際是遠程服務的本地代理。說白了就是經過動態代理來實現。負載均衡
(3)通訊:RPC框架與具體的協議無關。框架
(4)序列化:畢竟是遠程通訊,須要將對象轉化成二進制流進行傳輸。不一樣的RPC框架應用的場景不一樣,在序列化上也會採起不一樣的技術運維
RPC面臨的挑戰異步
在大規模服務化以前,應用可能只是經過RPC框架,簡單的暴露和引用遠程服務,經過配置URL地址進行遠程服務調用,路由則經過F5負載均衡器等進行簡單的負載均衡。
當服務愈來愈多的時候,服務的URL配置管理變得更加困難。單純的使用RPC就有點吃不消。因此在大規模分佈式集羣中,RPC只是做爲集羣的一個方法調用手段。例如在Hadoop的進程間交互都是經過RPC來進行的,好比Namenode與Datanode直接,Jobtracker與Tasktracker之間等。
RPC與Web Servie
講道理,我以爲RPC與Webservice很像.能夠說Web Service是在RPC發展的基礎之上。web service是運行在web上的一個服務
RPC使用C/S方式,發送請求到服務器,等待服務器返回結果。
Web Service提供的服務是基於web容器的,底層使用http協議,相似一個遠程的服務提供者,好比天氣預報服務,對各地客戶端提供天氣預報,是一種請求應答的機制,是跨系統跨平臺的。就是經過一個servlet,提供服務出去。
RPC與JMS
在RPC中,當一個請求到達RPC服務器時,這個請求就包含了一個參數集和一個文本值,一般造成「classname.methodname」的形式。這就向RPC服務器代表,被請求的方法在爲「classname」的類中,名叫「methodname」。而後RPC服務器就去搜索與之相匹配的類和方法,並把它做爲那種方法參數類型的輸入。這裏的參數類型是與RPC請求中的類型是匹配的。一旦匹配成功,這個方法就被調用了,其結果被編碼後返回客戶方。
JMS 通常只是一個點發出一個Message到Message Server,發出以後通常不會關心誰用了這個message。JMS能夠作到異步調用徹底隔離了客戶端和服務提供者,可以抵禦流量洪峯;JMS得到消息能夠進行延遲處理,而RPC通常是進行實時調用。
相比較其餘的通訊而言,RPC的側重點在於方法。而且是C/S架構方式
服務化架構的演進圖
MVC架構:當業務規模很小時,將全部功能都不熟在同一個進程中,經過雙機或者負載均衡器實現負債分流;此時,分離先後臺邏輯的MVC架構是關鍵。
PRC架構:當垂直應用愈來愈多,應用之間交互不可避免,將核心和公共業務抽取出來,做爲獨立的服務,實現先後臺邏輯分離。此時,用於提升業務複用及拆分的RPC框架是關鍵。
SOA架構:隨着業務發展,服務數量愈來愈多,服務生命週期管控和運行態的治理成爲瓶頸,此時用於提高服務質量的SOA服務治理是關鍵。
微服務架構:經過服務的原子化拆分,以及微服務的獨立打包、部署和升級,小團隊的交付週期將縮短,運維成本也將大幅度降低。