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服务治理是关键。
微服务架构:经过服务的原子化拆分,以及微服务的独立打包、部署和升级,小团队的交付周期将缩短,运维成本也将大幅度降低。