javarpc代码实现 java 实现rpc

rpc的实现机制是什么

RPC 的全称是 Remote Procedure Call 是一种进程间通信方式。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即无论是调用本地接口/服务的还是远程的接口/服务,本质上编写的调用代码基本相同。

创新互联专业为企业提供双辽网站建设、双辽做网站、双辽网站设计、双辽网站制作等企业网站建设、网页设计与制作、双辽企业网站模板建站服务,十载双辽做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。

比如两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或者方法,由于不在一个内存空间,不能直接调用,这时候需要通过就可以应用RPC框架的实现来解决。

RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)

RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)

RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。

二、常见RPC框架

几种比较典型的RPC的实现和调用框架。

(1)RMI实现,利用java.rmi包实现,基于Java远程方法协议(Java Remote Method Protocol)

和java的原生序列化。

(2)Hessian,是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 基于HTTP协议,采用二进制编解码。

(3)THRIFT是一种可伸缩的跨语言服务的软件框架。thrift允许你定义一个描述文件,描述数据类型和服务接口。依据该文件,编译器方便地生成RPC客户端和服务器通信代码。

二、RPC框架实现原理

在RPC框架中主要有三个角色:Provider、Consumer和Registry。如下图所示:

RPC框架面试总结-RPC原理及实现

节点角色说明:

* Server: 暴露服务的服务提供方。

* Client: 调用远程服务的服务消费方。

* Registry: 服务注册与发现的注册中心。

三、RPC调用流程

RPC基本流程图:

RPC框架面试总结-RPC原理及实现

一次完整的RPC调用流程(同步调用,异步另说)如下:

1)服务消费方(client)调用以本地调用方式调用服务;

2)client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;

3)client stub找到服务地址,并将消息发送到服务端;

4)server stub收到消息后进行解码;

5)server stub根据解码结果调用本地的服务;

6)本地服务执行并将结果返回给server stub;

7)server stub将返回结果打包成消息并发送至消费方;

8)client stub接收到消息,并进行解码;

9)服务消费方得到最终结果。

RPC框架的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明。

四、服务注册发现

RPC框架面试总结-RPC原理及实现

服务提供者启动后主动向注册中心注册机器ip、port以及提供的服务列表;

服务消费者启动时向注册中心获取服务提供方地址列表,可实现软负载均衡和Failover;

五、使用到的技术

1、动态代理

生成 client stub和server stub需要用到 Java 动态代理技术 ,我们可以使用JDK原生的动态代理机制,可以使用一些开源字节码工具框架 如:CgLib、Javassist等。

2、序列化

为了能在网络上传输和接收 Java对象,我们需要对它进行 序列化和反序列化操作。

* 序列化:将Java对象转换成byte[]的过程,也就是编码的过程;

* 反序列化:将byte[]转换成Java对象的过程;

可以使用Java原生的序列化机制,但是效率非常低,推荐使用一些开源的、成熟的序列化技术,例如:protobuf、Thrift、hessian、Kryo、Msgpack

关于序列化工具性能比较可以参考:jvm-serializers

3、NIO

当前很多RPC框架都直接基于netty这一IO通信框架,比如阿里巴巴的HSF、dubbo,Hadoop Avro,推荐使用Netty 作为底层通信框架。

4、服务注册中心

可选技术:

* Redis

* Zookeeper

* Consul

* Etcd

java用哪个组件实现的rpc

Thrift 是由 Facebook 开源的一个 RPC 框架,现在已经挂在 apache.org 下了。主要的几个好处:

1. 支持非常多语言,包括在 WEB 开发中很常用的 PHP,以及最重要的 C++/Python/Java 等 WEB后端常用语言,当然,还包括很 cool 的 Ruby、Erlang。

2. 完整的 RPC 框架实现,用脚本生成通讯相关的框架代码,开发者只需要集中精力处理好 业务逻辑。比如搭建一个 Hello World Service 只需要几分钟。

3.拥有被 Facebook、Last.fm 等不少大规模互联网应用验证过的性能和可用性。

Hessian是一款基于HTTP协议的RPC框架,采用的是二进制RPC协议,非常轻量级 ,且速度较快。

当然,还有Hetty,它是一款构建于Netty和Hessian基础上的高性能的RPC框架。

jsonrpc java怎么运行

JSON-RPC-Java 由两个对用户可视化的组件构成,它们是JSONRPCBridge和JSONRPCServlet,二者的协调实现了JSON服务器端对请求对象的处理,并响应给用户。

JSONRPCBridge是一个拥有服务器端导出给特定客户端的对象的引用的session对象,它接收服务器(也就是JSONRPCServlet)传来的JSON-RPC请求,然后其把JSON对象解码(unmarshalling)为Java对象,再扮演方法调用的角色,还要把方法调用返回的Java对象结果组码(marshall)为JSON对象传给客户端处理。而具体的从Java对象到Javascript对象的类型转换由负责串行化的串行化类Serializer处理。

JSONRPCBridge必须放置在HttpSession对象中,并且注册属性名为“JSONRPCBridge”,以使JSONRPCServlet能够定位负责调用导出到客户端的Java对象的桥。为此,为了导出一个对象的所有实例和静态方法到客户端,应该有如下的代码: JSONRPCBridge.registerObject("myObject", myObject);

为了导出一个类的所有的静态方法,应该:JSONRPCBridge.registerClass(("myObject", myObject);

如果registerObject和registerClass被多次调用为有相同键值的对象使用,那么其将被最新赋值的对象所更新。

在JSON中,还可以使用单例模式的globalBridge来为所有的HTTP客户端导出所有的实例方法。其可以用来注册工厂类,但使用时要注意认证与安全问题。使用时如下标识:JSONRPCBridge.getGlobalBridge().registerObject("myObject",myObject); 同上其还可以导出所有的静态方法。

该协议中最重要的一部分还有就是Servlet了,现在就来简单的介绍一下:

在该协议中,JSONRPCServlet充当传送器,处理通过HTTP传输过来的JSON

如何实现一个简单的RPC框

0,服务接口定义---Echo.java

/*

* 定义了服务器提供的服务类型 */public interface Echo {    public String echo(String string);

}

一,客户端代码分析--实现类:MainClient.java

客户端实现包括:获得一个代理对象,并使用该代理对象调用服务器的服务。获取代理对象时,需要指定被代理的类(相当于服务器端提供的服务名),Server IP,Port,这样客户端就能找到服务端的服务了。

延伸:分布式环境下,Client如何打到Server的服务?---因为,在服务器中运行的某些服务不像标准服务有着固定的端口,如HTTP的80端口。

一种解决方法是:在运行服务的每台机器上都运行一个特殊的守护进程,该守护进程负责跟踪位于该机器中每一项服务所使用的端口;此外,守护进程还监听一个特定的已经端口,Client通过这个端口与守护进程联系,请求得到指定服务的端口。

复杂的RPC实现框架中,比如可以把服务注册到ZooKeeper中,Client也从ZooKeeper中查询服务。参考:一个更复杂的RPC框架实现

Echo echo = RPC.getProxy(Echo.class, "127.0.0.1", 20382);

System.out.println(echo.echo("hello,hello"));//使用代理对象调用服务器的服务.并将结果输出

二,服务器端分析--实现类:MainServer.java

服务器实现包括:创建一个服务器对象,将它能提供的服务注册,并启动进程监听客户端的连接

Server server = new RPC.RPCServer();        /*

* server 启动后,需要注册server端能够提供的服务,这样client使用 服务的名字、

* 服务器的IP、以及服务所运行的端口 来调用 server 的服务         */

server.register(Echo.class, RemoteEcho.class);//注册服务的名字

server.register(AnOtherEchoService.class, AnOtherEchoServiceImpl.class);

server.start();//启动server

三,服务器监听Client连接分析----实现类:Listener.java

当server.start()后,它要创建一个Listener对象,这是一个线程类,该线程用来监听Client连接。

public void start() {

System.out.println("启动服务器");

/*

* server 启动时,需要Listener监听是否有client的请求连接

* listener 是一个线程,由它来监听连接             */

listener = new Listener(this);            this.isRuning = true;

listener.start();//listener 是一个线程类,start()后会执行线程的run方法

}

其实,监听连接就是JAVA ServerSocket类和Socket类提供的相关功能而已。

/*

* accept()是一个阻塞方法,server_socket 一直等待client 是否有连接到来 */

Socket client = server_socket.accept();//建立一条TCP连接

四,动态代理对象 生成---RPC.java

客户端只需要编写生成代理对象,用代理对象去调用远程服务的代码即可。但是,底层的功能如:建立连接,序列化(本例中也没有考虑),跨语言调用(未考虑)...是由RPC框架完成的。

当MainClient 语句:RPC.getProxy(Echo.class, "127.0.0.1", 20382);执行时,会由

/*

* @param Class[]{} 该参数声明了动态生成的代理对象实现了的接口,即 clazz 所代表的接口类型 .

* 这表明了生成的代理对象它是一个它所实现了的接口类型的对象

* 从而就可以用它来调用它所实现的接口中定义的方法

*

* @param handler 生成代理实例对象时需要传递一个handler参数

* 这样当该 代理实例对象调用接口中定义的方法时,将会委托给InvocationHandler 接口中声明的invoke方法

* 此时,InvocationHandler 的invoke 方法将会被自动调用         */

T t = (T) Proxy.newProxyInstance(RPC.class.getClassLoader(), new Class[] {clazz}, handler);        return t;

返回该代理对象,然后就会委托第三个参数 handler 自动执行 invoke(),invoke将客户端调用的所有相关信息封装到Invocation 对象中(后面分析)。然后执行第16行代码发起连接。

1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2                 Invocation invo = new Invocation(); 3                 invo.setInterfaces(clazz); 4                  5                 //利用反射机制将java.lang.reflect.Method 所代表的方法名,参数 封装到 Invocation invo对象中 6                 invo.setMethod(new org.jy.rpc.protocal.Method(method.getName(),method.getParameterTypes())); 7                 invo.setParams(args); 8                  9                 /*10                  * 当把需要调用的远程server端的方法名和参数封装到invo之后,Client 对象 就可以把 invo 作为参数 传递给服务器了.11                  * 为什么需要这样做呢?InvocationHandler 的invoke方法是自动执行的,在该方法里面,它根据生成的代理对象 proxy (第一个参数)12                  * 所实现的接口(由 Proxy.newProxyInstance()的第二个参数指定) 就可以知道这个接口中定义了哪些方法13                  * InvocationHandler 的 invoke 方法的第二个参数Method method 就可以解析出接口中的方法名和参数了14                  * 把它们封装进Invocation invo对象中,再将 invo 作为 client.invoke(invo)的参数 发送到服务器方15                  */16                 client.invoke(invo);//invoke 先调用init发起一个Socket连接,再将invo 发送至输出流中17                 return invo.getResult();18             }

五,“客户端存根”--Client.java

最重要的是它的 invoke方法(注意与InvocationHandler的invoke()区分)。它负责建立连接,打开输入、输出流,向服务器发送字节数据。

1     public void invoke(Invocation invo) throws UnknownHostException, IOException, ClassNotFoundException {2         init();3         System.out.println("写入数据");4         oos.writeObject(invo);//将Client 需要调用的Server的 接口、方法、参数 封装起来 发给服务器5         oos.flush();6         ois = new ObjectInputStream(socket.getInputStream());//用来接收从 server 返回 回来的执行结果 的输入流7         Invocation result = (Invocation) ois.readObject();8         invo.setResult(result.getResult());//将结果 保存到 Invocation result对象中9     }

六,“服务器存根“---实现类:RPCServer.java

上面提到,服务器通过Listener监听客户端连接,当建立客户端连接后,Socket client = server_socket.accept(); 不再阻塞,服务器调用它的call()方法完成客户端请求的功能。也即,客户端请求的结果实际上是在服务器执行生成的。返回的结果是在Client.java 的 invoke() 方法里被读取出来 。call()再一次用到了JAVA反射(第11行) 参考:JAVA动态代理

1 public void call(Invocation invo) { 2             System.out.println(invo.getClass().getName()); 3             Object obj = serviceEngine.get(invo.getInterfaces().getName()); 4             if(obj!=null) { 5                 try { 6                     Method m = obj.getClass().getMethod(invo.getMethod().getMethodName(), invo.getMethod().getParams()); 7                     /* 8                      * 利用JAVA反射机制来执行java.lang.reflect.Method 所代表的方法 9                      * @param result : 执行实际方法后 得到的 服务的执行结果10                      */11                     Object result = m.invoke(obj, invo.getParams());12                     invo.setResult(result);//将服务的执行结果封装到invo对象中。在后面的代码中,将该对象写入到输出流中13                 } catch (Throwable th) {14                     th.printStackTrace();15                 }16             } else {17                 throw new IllegalArgumentException("has no these class");18             }19         }

七,”RPC 编码、解码,协议的定义“---Invocation.java   Method.java

其实,这里并不是那种实用的开源RPC框架如Thrift中所指的编码、IDL……上面两个类只是RPC实现过程中辅助完成Java动态代理的实现,说白了就是封装客户端需要调用的方法,然后指定生成的代理对象需要实现的接口(服务).

八,总结:

先运行MainServer.java启动服务器,然后,再运行MainClient.java 启动一个客户端连接服务器就可以看到执行结果。

当需要添加新的服务时:按以下步骤即可:①定义服务接口及其实现类,如:AnOtherEchoService.java  ②:在MainServer.java中注册新添加的服务。

③:在MainClient.java中编写获得新服务的代理对象的代码,并用该代理对象调用新服务接口中声明的方法。

这样,在客户端就能够远程地调用服务器上的一个新服务了。


网站标题:javarpc代码实现 java 实现rpc
当前URL:http://scjbc.cn/article/dojhehd.html

其他资讯