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 相关文章推荐
editable.js 基于jquery的表格的编辑插件
Oct 24 Javascript
iframe异步加载实现点击左边菜单加载右边内容实例讲解
Mar 04 Javascript
jQuery中prevAll()方法用法实例
Jan 08 Javascript
使用ngView配合AngularJS应用实现动画效果的方法
Jun 19 Javascript
JavaScript html5 canvas绘制时钟效果
Mar 01 Javascript
javascript正则表达式中分组详解
Jul 17 Javascript
JavaScript中定义对象原型的两种使用方法
Dec 15 Javascript
es7学习教程之fetch解决异步嵌套问题的方法示例
Jul 21 Javascript
jquery.uploadView 实现图片预览上传功能
Aug 10 jQuery
webpack踩坑之路图片的路径与打包
Sep 05 Javascript
原生js实现trigger方法示例代码
May 22 Javascript
Node实现搜索框进行模糊查询
Jun 28 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
mysql中存储过程、函数的一些问题
2007/02/14 PHP
如何在php中正确的使用json
2013/08/06 PHP
php计算title标题相似比的方法
2015/07/29 PHP
基于thinkPHP类的插入数据库操作功能示例
2017/01/06 PHP
php使用pthreads v3多线程实现抓取新浪新闻信息操作示例
2020/02/21 PHP
file控件选择上传文件确定后触发的js事件是哪个
2014/03/17 Javascript
Javascript无参数和有参数类继承问题解决方法
2015/03/02 Javascript
在HTML中插入JavaScript代码的示例
2015/06/03 Javascript
Bootstrap表单简单实现代码
2017/03/06 Javascript
如何使用JS在HTML中自定义字符串格式化
2017/07/20 Javascript
基于JavaScript实现五子棋游戏
2020/08/26 Javascript
vuex实现登录状态的存储,未登录状态不允许浏览的方法
2018/03/09 Javascript
微信小程序如何再次获取用户授权的方法
2019/05/10 Javascript
vue移动端弹起蒙层滑动禁止底部滑动操作
2020/07/22 Javascript
jQuery实现简单三级联动效果
2020/09/05 jQuery
[47:12]TFT vs Secret Supermajor小组赛C组 BO3 第三场 6.3
2018/06/04 DOTA
python关闭windows进程的方法
2015/04/18 Python
举例讲解Python编程中对线程锁的使用
2016/07/12 Python
python自动登录12306并自动点击验证码完成登录的实现源代码
2018/04/25 Python
Django+Xadmin构建项目的方法步骤
2019/03/06 Python
python数据持久存储 pickle模块的基本使用方法解析
2019/08/30 Python
Python列表切片常用操作实例解析
2020/03/10 Python
详解H5 活动页之移动端 REM 布局适配方法
2017/12/07 HTML / CSS
LightInTheBox西班牙站点:全球商品在线采购
2016/09/22 全球购物
英国翻新电子产品购物网站:Tech Trade
2017/12/25 全球购物
Calzedonia美国官网:意大利风格袜子、打底裤和沙滩装
2018/07/19 全球购物
团代会宣传工作方案
2014/05/08 职场文书
六年级语文下册教学计划
2015/01/22 职场文书
战马观后感
2015/06/08 职场文书
欢送领导祝酒词
2015/08/12 职场文书
保险公司增员口号
2015/12/25 职场文书
pandas 操作 Excel操作总结
2021/03/31 Python
python 遍历磁盘目录的三种方法
2021/04/02 Python
MySQL 十大常用字符串函数详解
2021/06/30 MySQL
Python何绘制带有背景色块的折线图
2022/04/23 Python
Win10鼠标宏怎么设置?win10系统鼠标宏的设置方法
2022/08/14 数码科技