java 远程调用 RPC
1. 概念
RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Http invoker等。RPC是与语言无关的。直观说法就是A通过网络调用B的过程方法。也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
1、首先要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,B服务器的IP,以及应用绑定的端口,还有方法的名称,这样才能完成调用
2、方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式
3、在B服务器上完成寻址后,需要对参数进行反序列化,恢复为内存中的表达方式,然后找到对应的方法进行本地调用,然后得到返回值,
4、返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给应用
1.1 框架
1.1.1 RMI
java自带远程调用框架
1.1.2 THrift
thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。
1.1.3 Dubbo(Dubbox)
服务治理,也可以做微服务
1.1.4 gRPC
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
开源中国组织翻译的《gRPC 官方文档中文版》:http://doc.oschina.net/grpc
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
2. RPC的实现
RPC能够让本地应用简单、高效地调用服务器中的过程(服务)。它主要应用在分布式系统。如Hadoop中的IPC组件。但怎样实现一个RPC框架呢?
从下面几个方面思考,仅供参考:
1.通信模型:假设通信的为A机器与B机器,A与B之间有通信模型,在Java中一般基于BIO或NIO;。
2.过程(服务)定位:使用给定的通信方式,与确定IP与端口及方法名称确定具体的过程或方法;
3.远程代理对象:本地调用的方法(服务)其实是远程方法的本地代理,因此可能需要一个远程代理对象,对于Java而言,远程代理对象可以使用Java的动态对象实现,封装了调用远程方法调用;
4.序列化,将对象名称、方法名称、参数等对象信息进行网络传输需要转换成二进制传输,这里可能需要不同的序列化技术方案。如:protobuf,Arvo等。
2.1 实现技术方案
下面使用比较原始的方案实现RPC框架,采用Socket通信、动态代理与反射与Java原生的序列化。
2.2 RPC框架架构
RPC架构分为三部分:
1)服务提供者,运行在服务器端,提供服务接口定义与服务实现类。
2)服务中心,运行在服务器端,负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用。
3)服务消费者,运行在客户端,通过远程代理对象调用远程服务。
2.3 Demo
2.3.1 服务提供者接口定义
HelloService.java
package com.loveincode.rpc; public interface HelloService { String hello(String name); }
2.3.2 服务提供者接口实现
HelloServiceImpl.java
package com.loveincode.rpc; //HelloServices接口实现类:
public class HelloServiceImpl implements HelloService { public String hello(String name) {
return "hello, " + name;
} }
2.3.3 RPC框架服务中心
RpcFramework.java
package com.loveincode.rpc; 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.ServerSocket;
import java.net.Socket; public class RpcFramework {
/**
* 暴露服务
*
* @param service
* 服务实现
* @param port
* 服务端口
* @throws Exception
*/
public static void export(final Object service, int port) throws Exception {
if (service == null)
throw new IllegalArgumentException("service instance == null");
if (port <= 0 || port > 65535)
throw new IllegalArgumentException("Invalid port " + port);
System.out.println("Export service " + service.getClass().getName() + " on port " + port);
ServerSocket server = new ServerSocket(port);// 前面都是验证调用是否符合规则,这里在被调用端开一个服务。下面就是死循环运行。
for (;;) {
try {
final Socket socket = server.accept();
new Thread(new Runnable() {// 对每一个请求new一个线程,匿名类
@Override
public void run() {
try {
try {
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
Object[] arguments = (Object[]) input.readObject();
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());// 接收客户端传来的方法名、参数类型、参数
try {
Method method = service.getClass().getMethod(methodName, parameterTypes);// 在本地生成对应的方法,
Object result = method.invoke(service, arguments);// 调用
output.writeObject(result);// 返回结果
} catch (Throwable t) {
output.writeObject(t);
} finally {
output.close();
}
} finally {
input.close();
}
} finally {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
} /**
* 引用服务
*
* @param <T>
* 接口泛型
* @param interfaceClass
* 接口类型
* @param host
* 服务器主机名
* @param port
* 服务器端口
* @return 远程服务
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
if (interfaceClass == null)
throw new IllegalArgumentException("Interface class == null");
if (!interfaceClass.isInterface())
throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
if (host == null || host.length() == 0)
throw new IllegalArgumentException("Host == null!");
if (port <= 0 || port > 65535)
throw new IllegalArgumentException("Invalid port " + port);
System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] { interfaceClass },
new InvocationHandler() {// 用动态代理的方法进行包装,看起来是在调用一个方法,其实在内部通过socket通信传到服务器,并接收运行结果
public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
Socket socket = new Socket(host, port);
try {
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(arguments);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;// 返回结果
} finally {
input.close();
}
} finally {
output.close();
}
} finally {
socket.close();
}
}
});
}
}
2.3.4 服务提供者
RpcProvider.java
package com.loveincode.rpc; public class RpcProvider {
public static void main(String[] args) throws Exception {
HelloService service = new HelloServiceImpl();
RpcFramework.export(service, 1234);
}
}
2.3.5 服务消费者
RpcConsumer.java
package com.loveincode.rpc; public class RpcConsumer {
public static void main(String[] args) throws Exception {
HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String hello = service.hello("World " + i);
System.out.println(hello);
Thread.sleep(1000);
}
}
}
运行结果
执行RpcProvider 结果:
Export service com.loveincode.rpc.HelloServiceImpl on port 1234
执行RpcConsumer 结果:
Get remote service com.loveincode.rpc.HelloService from server 127.0.0.1:1234
hello, World 0
hello, World 1
hello, World 2
hello, World 3
hello, World 4
hello, World 5
hello, World 6
hello, World 7
hello, World 8
hello, World 9
...
java 远程调用 RPC的更多相关文章
- Atitit.分布式远程调用 rpc rmi CORBA的关系
Atitit.分布式远程调用 rpc rmi CORBA的关系 1. 远程调用(包括rpc,rmi,rest)1 2. 分布式调用大体上就分为两类,RPC式的,REST式的1 3. RPC(远程 ...
- netty实现远程调用RPC功能
netty实现远程调用RPC功能 依赖 服务端功能模块编写 客户端功能模块编写 netty实现远程调用RPC功能 PRC的功能一句话说白了,就是远程调用其他电脑的api 依赖 <dependen ...
- Java[2] 分布式服务架构之java远程调用技术浅析(转http://www.uml.org.cn/zjjs/201208011.asp)
转自:http://www.uml.org.cn/zjjs/201208011.asp 在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如: ...
- 远程调用RPC
一.简介 RPC,就是Remote Procedure Call的简称呀,翻译成中文就是远程过程调用. 本地调用,就好比你现在在家里,你要想洗碗,那你直接把碗放进洗碗机,打开洗碗机开关就可以洗了.这就 ...
- (转)RabbitMQ消息队列(七):适用于云计算集群的远程调用(RPC)
在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇文章中,我们将会 ...
- Java远程调用邮件服务器,实现邮件发送
写这篇文章的背景是公司Android客户端需要实现一个功能,实现类似于密码找回或者用户注册完发送一个邮件给用户的功能,当然这些逻辑客户端只负责请求自己的服务端,自己的服务端再去请求邮件服务器. 邮件服 ...
- RabbitMQ消息队列(七):适用于云计算集群的远程调用(RPC)
在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇 ...
- RabbitMQ 适用于云计算集群的远程调用(RPC)
在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇文章中,我们将会 ...
- java远程调用linux的命令或者脚本
转载自:http://eksliang.iteye.com/blog/2105862 Java通过SSH2协议执行远程Shell脚本(ganymed-ssh2-build210.jar) 使用步骤如下 ...
随机推荐
- js禁止选中(网页复制)
document.onselectstart=new Function("event.returnValue=false");//禁止选中(无法复制) document.oncon ...
- 【从零开始】用node搭建一个jsonp&json服务
目录: 一.介绍 二.node安装 三.webstorm配置node环境 四.代码介绍 五.如何使用 六.自定义域名 七.其他 一.介绍 1.背景 日常工作中,跟后端商定好接口格式后:通常采用 ...
- SpringJdbc持久层封装,Spring jdbcTemplate封装,springJdbc泛型Dao,Spring baseDao封装
SpringJdbc持久层封装,Spring jdbcTemplate封装,springJdbc泛型Dao,Spring baseDao封装 >>>>>>>& ...
- Lniux下安装mysql----编译版
####安装mysql-5.7.10rpm -e --nodeps mysqlrpm -e mysqlclient10useradd -g mysql -s /sbin/nologininstall_ ...
- lsdslam代码笔记
0.1. question 0.2. 算法框架 0.3. 代码解析 0.3.1. 数据结构 0.3.1.1. Frame 0.3.1.2. FrameMemory 0.3.1.3. FramePose ...
- WPF制作带明细的环形图标
效果 明细用Popup实现的,录gif时,Popup显示不出来,不知道为什么,所以静态图凑合看吧 大体思路 图表使用Arc+Popup实现 图表分为两部分,一是环形部分,一是标注的明细部分. 环形部分 ...
- maven 打包时mapper.xml打不进去问题
首先,来看下MAVENx项目标准的目录结构: 一般情况下,我们用到的资源文件(各种xml,properites,xsd文件等)都放在src/main/resources下面,利用maven打包时,ma ...
- Python运维开发基础-概述-hello world
Hello World 任何一门计算机语言几乎都是从hello world开始的,为了遵从这一优秀的传统习惯,我们也从打印hello world开始. linux系统进入Python环境,直接打Pyt ...
- 西电2017ACM网络赛
#include<bits/stdc++.h> using namespace std; typedef long long LL; #define ms(a,x) memset(a,x, ...
- nyoj_762:第k个互质数
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=762 直接给代码好了,容斥原理具体看<组合数学> #include<bi ...