node.js中RPC(远程过程调用)的实现原理介绍

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

node.js中RPC(远程过程调⽤)的实现原理介绍

刚接触到RPC(远程过程调⽤),就是可以在本地调⽤远程机⼦上的程序的⽅法,看到⼀个简单的nodejs实现,⽤来学习RPC的原理很不错:

使⽤⽰例:

复制代码代码如下:

//服务端

var light_rpc = require('./index.js');

var port = 5556;

var rpc = new light_rpc({

combine: function(a, b, callback){

callback(a + b);

},

multiply: function(t, cb){

cb(t*2);

}

}).listen(port);

Sample client:

复制代码代码如下:

//客户端

rpc.connect(5556, 'localhost', function(remote, conn){

bine(1, 2, function(res){

if(res != 3){

console.log('ERROR', res);

}

});

});

简单说说整个过程:

1.server端启动程序,侦听端⼝,实现提供给client调⽤的函数(如上述例⼦的combine和multiply),保存在⼀个对象⾥。

2.client端启动程序,连接服务端,连接完成后发送describe命令,要求server返回它能提供调⽤的函数名。

复制代码代码如下:

connection.on('connect', function(){

connection.write(command(descrCmd));

});

3.server端接收到describe命令,把⾃⼰可供调⽤的函数名包装好发送出去(“combine”, “multiply”)

4.client端接收到server发送的函数名,注册到⾃⼰的对象⾥,给每个函数名包装⼀个⽅法,使本地调⽤这些函数时实际上是向server端发送请求:

复制代码代码如下:

for(var p in cmd.data){

remoteObj[p] = getRemoteCallFunction(p, self.callbacks, connection);

//getRemoteCallFunction的实现见下⾯

}

5.client端调⽤server端的函数:

1) 给传⼊的callback函数⽣成⼀个唯⼀ID,称为callbackId,记录到client的⼀个对象⾥。

2) 包装好以下数据发送给server端:调⽤函数名,JSON序列化后的参数列表,callbackId

复制代码代码如下:

function getRemoteCallFunction(cmdName, callbacks, connection){

return function(){

var id = uuid.generate();

if(typeof arguments[arguments.length-1] == 'function'){

callbacks[id] = arguments[arguments.length-1];

}

var args = parseArgumentsToArray.call(this, arguments);

var newCmd = command(cmdName, {id: id, args: args});

connection.write(newCmd);

}

}

6.server端接收到上述信息,解析数据,对参数列表反序列化,根据函数名和参数调⽤函数。

复制代码代码如下:

var args = cmd.data.args;

args.push(getSendCommandBackFunction(c, cmd.data.id));

self.wrapper[mand].apply({}, args);

7.函数运⾏完成后,把结果序列化,连同之前收到的callbackId发送回client端

复制代码代码如下:

function getSendCommandBackFunction(connection, cmdId){

return function(){

var innerArgs = parseArgumentsToArray.call({}, arguments);

var resultCommand = command(resultCmd, {id: cmdId, args: innerArgs});

connection.write(resultCommand);

};

}

8.client端接收到函数运⾏结果和callbackId,根据callbackId取出回调函数,把运⾏结果传⼊回调函数中执⾏。

⼏个注意的点:

1.整个过程中client和server⼀直保持连接,不像http协议发送和接收完就断开链接,所以不能以断开链接判断⼀次数据的传送完成。为了判断数据接收完成,client和server发送的数据遵循⼀个简单的协议:在数据前加上数据包的长度和分隔符,如定分隔符为\n:[数据包长度\n数据],这样在收到数据后⾸先取出数据包的长度,再不断判断累计已接收到的数据包是否等于或超过这个长度,若是则⼀次数据传送完成,可以开始解析提取数据。

2.这个RPC简单在于没有考虑参数⾥有函数类型的情况,例如有参数是⼀个object,这个object下有函数成员,JSON序列化时会把函数忽略,在server端是执⾏不了这个函数的。

为了解决这个问题,需要进⾏复杂的处理:

1.深度遍历每个要发送给远端的参数,把函数成员抽出来,给这个函数⽣成唯⼀id,放到本地⼀个对象⾥,把这个函数成员替换成这个id字符串,并标识这个成员实际上是⼀个函数。这样这个对象就可以序列化发送出去了。

2.server接收到调⽤,当要使⽤参数object⾥的函数时,判断到这是⼀个经过client处理过的函数,有⼀个id,把这个id发送回client端,并⽤同样的⽅法把⾃⾝的回调函数id传给client,等待client端的回调。

3.client端接收到这个函数id,找到这个函数实体,调⽤,完成后根据server端给的回调id发送回给server端

4.server端收到结果,找到回调函数,继续执⾏,完成。

函数的记录⽅法可以以其他⽅式完成,⼤体思路就是把函数替换成可序列化的东西,记录函数以便remote端调⽤时能在本地找到这个函数。可以参考dnode的实现。

相关文档
最新文档