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


Posted in Javascript onDecember 05, 2014

刚接触到RPC(远程过程调用),就是可以在本地调用远程机子上的程序的方法,看到一个简单的nodejs实现,用来学习RPC的原理很不错:nodejs light_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){

    remote.combine(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[cmd.command].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取出回调函数,把运行结果传入回调函数中执行。

9.整个过程完成,详见源码:https://github.com/romulka/nodejs-light_rpc

几个注意的点:

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的实现。

Javascript 相关文章推荐
利用js实现在浏览器状态栏显示访问者在本页停留的时间
Dec 29 Javascript
JS判断文本框内容改变事件的简单实例
Mar 07 Javascript
javascript自动恢复文本框点击清除后的默认文本
Jan 12 Javascript
AngularJS 2.0新特性有哪些
Feb 18 Javascript
JS遍历页面所有对象属性及实现方法
Aug 01 Javascript
Bootstrap 实现查询的完美方法
Oct 26 Javascript
利用CSS、JavaScript及Ajax实现图片预加载的方法
Nov 29 Javascript
Node.js引入UIBootstrap的方法示例
May 11 Javascript
VUE简单的定时器实时刷新的实现方法
Jan 20 Javascript
jquery实现图片无缝滚动 蒙版遮蔽效果
Jan 11 jQuery
JS highcharts实现动态曲线代码示例
Oct 16 Javascript
详解JavaScript中的this指向问题
Feb 05 Javascript
node.js中实现同步操作的3种实现方法
Dec 05 #Javascript
node.js实现BigPipe详解
Dec 05 #Javascript
js实现点击添加一个input节点
Dec 05 #Javascript
Node.js实现的简易网页抓取功能示例
Dec 05 #Javascript
浅谈js的setInterval事件
Dec 05 #Javascript
浅谈javascript中createElement事件
Dec 05 #Javascript
javascript的push使用指南
Dec 05 #Javascript
You might like
php使用substr()和strpos()联合查找字符串中某一特定字符的方法
2015/05/12 PHP
php实现文本数据导入SQL SERVER
2015/05/17 PHP
yii用户注册表单验证实例
2015/12/26 PHP
JS实现打开本地文件或文件夹
2021/03/09 Javascript
ext监听事件方法[初级篇]
2008/04/27 Javascript
jQuery Autocomplete自动完成插件
2010/07/17 Javascript
Jquery 过滤器(first,last,not,even,odd)的使用
2014/01/22 Javascript
使用JQuery FancyBox插件实现图片展示特效
2015/11/16 Javascript
jQuery实现选项联动轮播效果【附实例】
2016/04/19 Javascript
AngularJS基础 ng-include 指令示例讲解
2016/08/01 Javascript
AngularJS 服务详细讲解及示例代码
2016/08/17 Javascript
JS禁止查看网页源代码的实现方法
2016/10/12 Javascript
javascript设计模式之中介者模式学习笔记
2017/02/15 Javascript
jQuery 利用ztree实现树形表格的实例代码
2017/09/27 jQuery
实现一个完整的Node.js RESTful API的示例
2017/09/29 Javascript
vue.js 微信支付前端代码分享
2018/02/10 Javascript
Vue中的v-for循环key属性注意事项小结
2018/08/12 Javascript
elementUI Vue 单个按钮显示和隐藏的变换功能(两种方法)
2018/09/04 Javascript
深入理解js A*寻路算法原理与具体实现过程
2018/12/13 Javascript
Vue中使用JsonView来展示Json树的实例代码
2020/11/16 Javascript
[51:00]Secret vs VGJ.S 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
关于Python 3中print函数的换行详解
2017/08/08 Python
python通过socket实现多个连接并实现ssh功能详解
2017/11/08 Python
python用列表生成式写嵌套循环的方法
2018/11/08 Python
Django集成搜索引擎Elasticserach的方法示例
2019/06/04 Python
解决pycharm 工具栏Tool中找不到Run manager.py Task的问题
2019/07/01 Python
python区块及区块链的开发详解
2019/07/03 Python
解决django接口无法通过ip进行访问的问题
2020/03/27 Python
pyinstaller打包单文件时--uac-admin选项不起作用怎么办
2020/04/15 Python
selenium设置浏览器为headless无头模式(Chrome和Firefox)
2021/01/08 Python
使用Python获取爱奇艺电视剧弹幕数据的示例代码
2021/01/12 Python
Melijoe时尚童装德国官网:Melijoe德国
2016/09/03 全球购物
GAZMAN官网:澳大利亚领先的男装品牌
2019/12/19 全球购物
2015年中学团委工作总结
2015/07/22 职场文书
springcloud整合seata
2022/05/20 Java/Android
css清除浮动clearfix:after的用法详解(附完整代码)
2023/05/21 HTML / CSS