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 相关文章推荐
jQuery对象数据缓存Cache原理及jQuery.data方法区别介绍
Apr 07 Javascript
Jquery显示、隐藏元素以及添加删除样式
Aug 09 Javascript
Javascript 按位取反运算符 (~)
Feb 04 Javascript
关于Javascript 对象(object)的prototype
May 09 Javascript
JavaScript改变CSS样式的方法汇总
May 07 Javascript
jQuery+PHP实现可编辑表格字段内容并实时保存
Oct 09 Javascript
Bootstrap源码解读导航条(7)
Dec 23 Javascript
VUE利用vuex模拟实现新闻点赞功能实例
Jun 28 Javascript
vue非父子组件通信问题及解决方法
Jun 11 Javascript
微信小程序实现带参数的分享功能(两种方法)
May 17 Javascript
小程序接入腾讯位置服务的详细流程
Mar 03 Javascript
详解在Vue.js编写更好的v-for循环的6种技巧
Apr 14 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 服务器配置(使用Apache及IIS两种方法)
2009/06/01 PHP
一些 PHP 管理系统程序中的后门
2009/08/05 PHP
php生成随机字符串可指定纯数字、纯字母或者混合的
2014/04/18 PHP
php实现获取局域网所有用户的电脑IP和主机名、及mac地址完整实例
2014/07/18 PHP
如何在Laravel5.8中正确地应用Repository设计模式
2019/11/26 PHP
犀利的js 函数集合
2009/06/11 Javascript
javascript 模式设计之工厂模式详细说明
2010/05/10 Javascript
使用jquery的ajax需要注意的地方dataType的设置
2013/08/12 Javascript
详解JavaScript正则表达式中的global属性的使用
2015/06/16 Javascript
浅析C/C++,Java,PHP,JavaScript,Json数组、对象赋值时最后一个元素后面是否可以带逗号
2016/03/22 Javascript
jQuery实现滚动条滚动到子元素位置(方便定位)
2017/01/08 Javascript
node.js爬虫爬取拉勾网职位信息
2017/03/14 Javascript
bootstrap警告框示例代码分享
2017/05/17 Javascript
Angular排序实例详解
2017/06/28 Javascript
说说AngularJS中的$parse和$eval的用法
2017/09/14 Javascript
bootstrap实现二级下拉菜单效果
2017/11/23 Javascript
浅谈mvvm-simple双向绑定简单实现
2018/04/18 Javascript
Vue项目自动转换 px 为 rem的实现方法
2018/10/29 Javascript
微信小程序实现选项卡效果
2018/11/06 Javascript
js replace替换字符串同时替换多个方法
2018/11/27 Javascript
详解从react转职到vue开发的项目准备
2019/01/14 Javascript
JavaScript实现世界各地时间显示
2020/09/07 Javascript
[01:38]【DOTA2亚洲邀请赛】Sumail——梦开始的地方
2017/03/03 DOTA
简单介绍Python中的RSS处理
2015/04/13 Python
Python选课系统开发程序
2016/09/02 Python
使用python编写简单的小程序编译成exe跑在win10上
2018/01/15 Python
python主线程捕获子线程的方法
2018/06/17 Python
python实现输入数字的连续加减方法
2018/06/22 Python
Django框架视图层URL映射与反向解析实例分析
2019/07/29 Python
python Pillow图像处理方法汇总
2019/10/16 Python
草莓网英国官网:Strawberrynet UK
2017/02/12 全球购物
瑞士灯具购物网站:Lampenwelt.ch
2018/07/08 全球购物
英国比较机场停车场网站:Airport Parking Essentials
2019/12/01 全球购物
税务专业毕业生自荐信
2013/11/10 职场文书
拾金不昧表扬信怎么写
2015/05/04 职场文书
如何使用Tkinter进行窗口的管理与设置
2021/06/30 Python