RPC(Remote Promote Call) 一種進程間通信方式。允許像調用本地服務一樣調用遠程服務。
RPC框架的主要目標就是讓遠程服務調用更簡單、透明。RPC框架負責屏蔽底層的傳輸方式(TCP或者UDP)、序列化方式(XML/JSON/二進制)和通信細節。開發人員在使用的時候只需要了解誰在什么位置提供了什么樣的遠程服務接口即可,並不需要關心底層通信細節和調用過程。
RPC框架原理圖
簡單的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服務治理是關鍵。
微服務架構:通過服務的原子化拆分,以及微服務的獨立打包、部署和升級,小團隊的交付周期將縮短,運維成本也將大幅度下降。