刚接触到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取出回调函数,把运行结果传入回调函数中执行。
 
self.callbacks[cmd.data.id].apply(this, cmd.data.args);
9.整个过程完成,详见源码:https://github.com/romulka/nodejs-light_rpc
 
 
 
几个注意的点:
 
1.整个过程中client和server一直保持连接,不像http协议发送和接收完就断开链接,所以不能以断开链接判断一次数据的传送完成。为了判断数据接收完成,client和server发送的数据遵循一个简单的协议:在数据前加上数据包的长度和分隔符,如定分隔符为/n:[数据包长度/n数据],这样在收到数据后首先取出数据包的长度,再不断判断累计已接收到的数据包是否等于或超过这个长度,若是则一次数据传送完成,可以开始解析提取数据。
 
2.这个RPC简单在于没有考虑参数里有函数类型的情况,例如有参数是一个object,这个object下有函数成员,JSON序列化时会把函数忽略,在server端是执行不了这个函数的。
 
为了解决这个问题,需要进行复杂的处理:
 
深度遍历每个要发送给远端的参数,把函数成员抽出来,给这个函数生成唯一id,放到本地一个对象里,把这个函数成员替换成这个id字符串,并标识这个成员实际上是一个函数。这样这个对象就可以序列化发送出去了。
server接收到调用,当要使用参数object里的函数时,判断到这是一个经过client处理过的函数,有一个id,把这个id发送回client端,并用同样的方法把自身的回调函数id传给client,等待client端的回调。
client端接收到这个函数id,找到这个函数实体,调用,完成后根据server端给的回调id发送回给server端
server端收到结果,找到回调函数,继续执行,完成。
函数的记录方法可以以其他方式完成,大体思路就是把函数替换成可序列化的东西,记录函数以便remote端调用时能在本地找到这个函数。可以参考dnode的实现。

[node.js]RPC(远程过程调用)的实现原理的更多相关文章

  1. 基于thrift的node.js rpc服务

    1.在node.js 服务下创建node_modules文件,npm install  thrift 下载thrift到该文件下. 2.编写idl文件.user.thrift 内容如下: struct ...

  2. RPC远程过程调用机制底层原理

  3. node.js学习笔记(三)——事件循环

    要理解事件循环,首先要理解事件驱动编程(Event Driven Programming).它出现在1960年.如今,事件驱动编程在UI编程中大量使用.JavaScript的一个主要用途是与DOM交互 ...

  4. Node.js 入门到干活,10 个优质项目就够了!

    Node.js 在很多大公司都有不错的实践,比如:淘宝.天猫 Web 版,很多页面都是在 Node 服务器上渲染的.还有各种脚手架.前端打包发布工具.构建生态的小工具,也基本都是 Node.js 编写 ...

  5. 从发布订阅模式入手读懂Node.js的EventEmitter源码

    前面一篇文章setTimeout和setImmediate到底谁先执行,本文让你彻底理解Event Loop详细讲解了浏览器和Node.js的异步API及其底层原理Event Loop.本文会讲一下不 ...

  6. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  7. Node.js原生及Express方法实现注册登录原理

    由于本文只是实现其原理,所以没有使用数据库,只是在js里面模拟数据库,当然是种中还是需要用数据库的. 1.node.js原生方法 ①html页面,非常简单,没有一丝美化~我们叫它user.html & ...

  8. 深入研究Node.js的底层原理和高级使用

    深入研究Node.js的底层原理和高级使用

  9. Atitit sleep原理 node.js sleep解决方案 timer

    Atitit  sleep原理  node.js sleep解决方案  timer sleep()的实现分为三步: 1.注册一个信号signal(SIGALRM,handler).接收内核给出的一个信 ...

随机推荐

  1. ASP.NE的缓存技术提高Web站点的性能

    一:我们为什么要使用缓存? 先来理解一下asp.net缓存技术的基本原理:把访问频繁的数据以及需要花大量的时间来加载的数据缓存在内存中,那么用户在下次请求同样的数据时,直接将内存中的数据返回给用户,从 ...

  2. Hibernate4.x之映射关系--多对多

    n-n的关联关系分为以下两类: 1.单向n-n关联关系: n-n的关联必须使用连接表 与1-n映射类似,必须为set集合元素添加key子元素,指定CATEGORIES_ITEMS表中参照CATEGOR ...

  3. SharePoint中的权限体系

    转:http://blog.csdn.net/yl_99/article/details/7767053 1.MOSS中的权限结构 MOSS中的权限结构主要有三部分:网站权限,列表权限,个人权限. 网 ...

  4. python引用在函数传参时的体现以及可变与不可变对象的对比

    今天偶然看到 vamei 老师的博客 http://www.cnblogs.com/vamei/archive/2012/07/10/2582795.html 讲的是python动态类型以及引用的事儿 ...

  5. OpenGL和pcDuino搭建数字示波器

    看到大神们用Arduino.AVR做示波器,感觉很好玩,手头的pcDuino能不能做呢?一不做二不休,现在我们就自己用pcDuino做一个. 硬件清单: pcDuino一块 杜邦线若干 软件环境: 1 ...

  6. 问题:关于一个贴友的js留言板的实现

    需求:用js做一个简单的留言板效果 html部分: 1: <!DOCTYPE> 2: <html lang="zh-en"> 3: <head> ...

  7. python学习之copy模块

    Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块. 1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象.2. copy.deepc ...

  8. wuzhicms 自定义SQL 标签

    {wz:sql sql="select * from wz_guestbook"} {loop $rs $r} {$r[title]} {/loop} {/wz} 自定义统计条数: ...

  9. 2015长春 HDU 5531 Rebuild

    题意:n个顶点组成的多边形能否形成正多边形? #include <cstdio> #include <cstring> #include <cmath> #incl ...

  10. 洛谷 P2279 03湖南 消防局的设立

    2016-05-30 16:18:17 题目链接: 洛谷 P2279 03湖南 消防局的设立 题目大意: 给定一棵树,选定一个节点的集合,使得所有点都与集合中的点的距离在2以内 解法1: 贪心 首先D ...