爱智中的 RPC,使实际场景中的开发变得如此简单
什么是 RPC?
RPC 全称 Remote Procedure Call —— 远程过程调用。RPC 技术能够使得远程调用像本地调用一样简单方便。为了更好的理解 RPC,举个例子:某个 程序 S 实现了一个全局资源计数器 add 函数,每次资源被使用则调用该函数使计数器加 1,如:
function add (num) {
return num++;
}
const num = add(1); // num = 2
如果全局资源被 程序 S 自己使用,那么仅需要在本程序中通过 require 引用 add 函数所在的模块并直接调用 add 函数即可。但如果该资源被另一个 程序 X 使用,程序 X 占用了该全局资源并试图改变资源计数器的值,就需要通过某种间接机制调用到 add 函数。我们可能会在 程序S 暴露出一个 Restful 接口并内部调用 add 方法将计算结果返回接口调用者。这种形式固然可行,但可能引入不必要的技术细节,并且 HTTP 请求处理逻辑也相对繁琐。那么 RPC 就刚好解决了这个问题,使调用者不需要关注具体请求过程,参数传递解析过程等,只需要像调用本地方法一样,就可以实现对远程方法的调用。
此外,如下文介绍,JSRE RPC 模块还提供了双工数据流 RPC 调用。与 HTTP 请求相比,实时性更好,也无须考虑 Body 大小限制、请求认证等问题,用起来简直不要太方便!
JSRE 中的 RPC
JSRE 提供了内置的 RPC 模块是非常强大的,主要用于应用程序和系统服务进程间的通信。该模块实现了两种远程过程调用服务:
-
使用
AF_UNIX类型套接字的多进程调用服务 -
使用
UDP协议的网络远程调用服务
并且支持以下功能:
-
请求和响应。
-
订阅和发布。
-
双工数据流(仅适用于
AF_UNIX类型)。
01如何使用
-
RPC服务使用流程

用户可以使用以下代码导入 Rpc 模块。
// Rpc 模块
const Rpc = require('rpc');
-
Rpc.Server
Rpc.Server 为 RPC 服务类,该方法支持创建两种类型服务:
1.使用 AF_UNIX 多进程通信创建 RPC 服务器。
new Rpc.Server(name[, errCallback[, onlyPrivilege]])
-
name{String} RPC 服务名称。由字母、数字和下划线组成,不超过 64 个字节。 -
errCallback{Function} 错误回调。默认值:undefined。 -
onlyPrivilege{Boolean}只有特权任务才能访问此 RPC 服务。默认值:false。 -
返回:{Object} RPC 服务器对象。
const Rpc = require('rpc');
const server = new Rpc.Server('LocalServer_1');
2.使用 UDP 协议创建 RPC 服务器
new Rpc.Server(saddr[, errCallback[, onlyPrivilege]])
-
saddr{Object} RPC 服务地址。 -
errCallback{Function} RPC 服务 I/O 错误回调。默认值:undefined。 -
onlyPrivilege{Boolean} 只有特权任务才能访问此 RPC 服务。默认值:false。 -
返回:{Object} RPC 服务器对象。
const Rpc = require('rpc');
const socket = require('socket');
const saddr = socket.sockaddr('127.0.0.1', 3000);
const server = new Rpc.Server(saddr);
-
Rpc 服务器实例
使用 new Rpc.Server() 创建的即为 RPC 服务器实例
server.on(event, callback)
RPC 服务器对象继承自EventEmitter,可使用on() 监听事件,客户端可以通过下文的client.call 等方法以该事件名称发起远程调用并触发此 server 事件。
const Rpc = require('rpc');
const server = new Rpc.Server('LocalServer_1');
server.on('command_echo', (msg, from, seq) => {
console.log('receive command_echo', msg, from);
server.reply({ res: 'ok', msg }, from, seq);
});
其中:
-
msg{Object} 客户端发送的消息或者可以理解为调用参数。
-
from{Object} 客户端信息。 -
seq{Integer} 自增的客户端调用序列号。
server.reply(msg, to, seq[, timeout])
向客户端发送回应
-
msg{Object} 回复消息对象。
-
to{Object} 回复客户端目标,直接使用 server.on 中的 from 对象即可。
-
seq{Integer} 客户端命令序列号,必须与客户端请求消息序列号相同。 -
timeout{Integer} 以毫秒为单位的等待超时。默认值:undefined,意味着永远等待。 -
返回:{Boolean}是否回复消息发送成功。
server.reverse(event, msg, to[, timeout])
触发客户端的事件,向指定的客户端发送消息
-
event{String} 要触发的客户端事件。
-
msg{Object} 反向消息对象。
-
t{Object} 反向目标,与reply类似,直接使用from对象即可。
-
timeout{Integer}以毫秒为单位的等待超时。默认值:未定义意味着永远等待。
-
返回:{Boolean}是否回复消息发送成功。
const Rpc = require('rpc');
const server = new Rpc.Server('LocalServer_1');
server.on('command_echo', (msg, from, seq) => {
console.log('receive command_echo', msg, from);
server.reverse('command_client', { res: 'ok', msg }, from);
});
server.createReadStream(clientPid[, alive])
创建一个临时ReadStream 对象来接收客户端发送的流式数据,一般对应 client.createWriteStream(rid[, async]) 使用。alive 是等待客户端 WriteStream 连接的时间。如果客户端在此时间间隔内未连接,ReadStream 则会生成 'Peer timeout!' 错误。最小时间为:1000。
-
clientPid{Integer} 指定客户端进程 ID,可通过 from.pid 获取。
-
alive{Integer} 最大空闲时间(毫秒)。默认值:30000。
-
返回:{ReadStream}
Readable流对象。
const Rpc = require('rpc');
const server = new Rpc.Server('LocalServer_1');
server.on('to_server', (msg, from, seq) => {
const r = server.createReadStream(from.pid);
const f = fs.createWriteStream('./save.txt');
r.on('error', function(error) {
console.error('Error:', error.message);
f.destroy(error);
});
f.on('finish', function() {
console.log('File save ok!');
});
r.pipe(f);
server.reply({ res: 'ok', id: r.id }, from, seq);
});
server.createWriteStream(rid[, async])
创建一个临时 WriteStream 对象以将流式数据发送到客户端,一般对应 client.createReadStream([alive]) 使用。
-
rid{String} 对应的客户端传递的ReadStreamID。
-
async{Boolean} 是否使用异步模式,效率较低,但对其他事件公平。默认值:true。
-
返回:{WriteStream}
Writable流对象。
const Rpc = require('rpc');
const server = new Rpc.Server('LocalServer_1');
server.on('to_client', (msg, from, seq) => {
const s = server.createWriteStream(msg.id);
const f = fs.createReadStream('./save.txt');
s.on('error', function(error) {
console.error('Error:', error.message);
f.destroy(error);
});
s.on('finish', function() {
console.log('File send ok!');
});
f.on('error', function(error) {
console.error('Error:', error.message);
s.destroy(error);
})
f.pipe(s);
server.reply({ res: 'ok' }, from, seq);
});
server.close()
关闭 RPC 服务。RPC 服务在关闭后不再允许使用。
const Rpc = require('rpc');
const server = new Rpc.Server('LocalServer_1');
// 关闭服务
server.close()
-
Rpc.Client
Rpc.Client 为 RPC 客户端类,该方法支持创建两种类型客户端:
-
使用
AF_UNIX多进程通信创建 RPC 客户端
new Rpc.Client(name[, errCallback])
-
name{String} RPC 服务名称。由字母、数字和下划线组成,不超过 64 个字节。
-
errCallback{Function} LPC 客户端 I/O 错误回调。默认值:undefined。
-
返回:{Object} RPC 客户端对象。
const Rpc = require('rpc');
const client = new Rpc.Client('LocalServer_1');
2.使用 UDP 协议创建 RPC 客户端
new Rpc.Client(saddr[, errCallback])
-
saddr{Object} RPC 服务器套接字地址。
-
errCallback{Function} LPC 客户端 I/O 错误回调。默认值:undefined。
-
返回:{Object} RPC 客户端对象。
const Rpc = require('rpc');
const socket = require('socket');
const saddr = socket.sockaddr('127.0.0.1', 3000);
const client = new Rpc.Client(saddr);
-
Rpc 客户端实例
使用 new Rpc.Server() 创建的即为 RPC 服务
client.call(event, msg[, callback[, timeout])
触发服务端监听的事件,向服务器发送调用请求
-
event{String} 要触发的服务器事件。
-
msg{Object}发送的消息。
-
callback{Function} 服务器回复回调函数。-
reply{Object} 服务器回复消息。undefined表示在指定的超时时间内未收到服务器响应。
-
-
timeout{Integer} 以毫秒为单位的等待超时。默认值:60000。
-
返回:{Boolean} 是否回复消息发送成功。
const Rpc = require('rpc');
const client = new Rpc.Client('LocalServer_1');
client.call('command_echo', { foo: 'foo' }, (reply) => {
console.log('Server reply:', reply);
});
const Rpc = require('rpc');
const client = new Rpc.Client('LocalServer_1');
const reply = client.callSync('command_echo', { foo: 'foo' });
if (reply) {
console.log('Server reply:', reply.msg);
}
client.fetch(event, msg[, timeout])
向服务器发送一个调用请求,这个函数是异步请求并返回一个Promise对象。
-
event{String} 要触发的服务器事件。
-
msg{Object}发送的消息。
-
timeout{Integer} 以毫秒为单位的等待超时。默认值:60000。
-
返回:{Promise} Promise 对象。
注意:此方法对服务器响应规则具有以下约定:
-
服务器返回对象中的 res 字段为 'ok',表示调用成功。
-
当调用失败时,info 字段会被返回,并且该字段必须是字符串,说明失败的原因。
const Rpc = require('rpc');
const client = new Rpc.Client('LocalServer_1');
async function echo() {
const reply = await client.fetch('command_echo', { foo: 'bar' });
console.log(reply.msg);
}
client.createReadStream([alive])
创建一个临时WriteStream对象以将数据发送到服务器。通常对应server.createReadStream(clientPid[, alive]) 使用。
-
rid{String} 对等ReadStreamID。
-
async{Boolean} 是否使用异步模式,效率较低,但对其他事件公平。默认值:true。 -
返回:{WriteStream}
Writable流对象。
const Rpc = require('rpc');
const client = new Rpc.Client('LocalServer_1');
const r = client.createReadStream();
client.call('to_client', { id: r.id }, function(reply) {
if (reply && reply.res === 'ok') {
const f = fs.createWriteStream('./xxx.txt');
r.on('error', function(error) {
console.error('Error:', error.message);
f.destroy(error);
});
f.on('finish', function() {
console.log('File save ok!');
});
r.pipe(f);
}
});
client.createWriteStream(rid[, async])
创建一个临时WriteStream对象以将数据发送到服务器。
-
rid{String} 对应服务端返回的ReadStreamID。
-
async{Boolean} 是否使用异步模式,效率较低,但对其他事件公平。默认值:true。
-
返回:{WriteStream}
Writable流对象。
const Rpc = require('rpc');
const client = new Rpc.Client('LocalServer_1');
client.call('to_server', {}, function(reply) {
if (reply && reply.res === 'ok') {
const s = client.createWriteStream(reply.id);
const f = fs.createReadStream('./xxx.txt');
s.on('error', function(error) {
f.destroy(error);
});
s.on('finish', function() {
console.log('File send ok!');
});
f.pipe(s);
}
});
client.close()
关闭 RPC 客户端, RPC 客户端对象关闭后不再允许使用
const Rpc = require('rpc');
const client = new Rpc.Client('LocalServer_1');
// 关闭客户端
client.close();
Rpc.MAX_MSG_SIZE
RPC 单条消息的最大长度,通常为 16 KB。
const Rpc = require('rpc');
Rpc.MAX_MSG_SIZE = 16;
有了 RPC 就可以使我们实际场景中的一些开发变得简单,比如:我们有多个应用要根据不用的场景同时控制一台智能灯,我们只需要启动一个专门控制智能灯的 RPC 服务,然后各个业务服务可以分别创建 RPC 客户端,去调用 RPC 服务,非常方便实现复用。
相关推荐
rpc_ts:TypeScript中的远程过程调用变得简单:crossed_fingers:
rpc_ts:TypeScript中的远程过程调用变得简单 :crossed_fingers: rpc_ts是用于在TypeScript中进行类型安全的的框架。 它不使用诸如或类:服务全部使用功能强大的TypeScript类型系统定义。 此方法特别适合于缩短依赖TypeScript进行前端和后端开发的同构Web应用程序的开发周期。 rpc_ts支持一元调用和服务器端流传输,并且与协议(流行的针对Web的gRPC协议的改编)完全兼容。 它的设计始终专注于模块化,简单性和健壮性,并且不需要使用中间gRPC代理。 有关更多信息,请参见和。 例子 聊天室 聊天室示例展示了rpc_ts的错误处理和实时功能,以及最佳实践。 底漆 这是带有一个过程getHello的最小RPC服务的样子: import { NodeHttpTransport } from '@improbable-eng/grpc-web-node-ht
cpp-rpc:简单的 C++ rpc 库
这是什么? 这是我的博客文章的 C++ rpc 示例程序。 如何运行这个例子? 将 BOOST_INCLUDE、BOOST_LIB64 宏设置为正确的路径。
python实现一个简单RPC框架的示例
本文需要一点Python socket基础。 回顾RPC 客户端(Client):服务调用方。 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。 服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。 服务端(Server):服务的真正提供者。 Network Service:底层传输,可以是 TCP 或 HTTP。 实现jsonrpc 在实现前,简单理一下整体思路。 1、Network Service 直接使用Python Socket相关
0490-极智开发-解读使用tvm交叉编译和RPC部署在raspberry和rk3399教程
0490_极智开发_解读使用tvm交叉编译和RPC部署在raspberry和rk3399教程
实现一个简单的RPC框架
使用socket/反射/序列化等技术实现了一个基本的远程调用框架,可参考博客http://blog.csdn.net/u013177446/article/details/66473066



