4.4 Nodejs与R跨平台通信

问题

Nodejs怎么调用R?

引言

做Web开发还没有用过Nodejs的同学已经落后了。Nodejs是基于Javascript实现的一款后台程序开发平台,以笔者个人的体验来说,Nodejs的开发效率比PHP还要高一些,完全异步加载,性能一点也不落后,假以时日还有赶超PHP的趋势。

HTML5作为Web前端大量使用Javascript,各种炫效果层出不穷。如果能把R语言的效果图,都用HTML5重新做渲染,并增加时时通信和用户交互,两种语言各取优势,必然产生惊为天人的效果。长话短说,下面就来介绍 Nodejs与R跨平台通信

4.4.1 Nodejs简单介绍

Nodejs是一个可以快速构建网络服务及应用的平台,基于Chrome’s JavaScript runtime,也就是说,实际上它是对Google V8引擎(应用于Google Chrome浏览器)进行了封装。V8引擎执行Javascript的速度非常快,性能非常好。Nodejs对一些特殊用例进行了优化,提供了替代的API,使得V8在非浏览器环境下运行得更好。

4.4.2 R语言配置环境

本节中介绍的Nodejs与R的跨平台通信,对于R语言的支持库是Rserve,通过Rserve提供一个R的TCP/IP的通信协议,实现Nodejs和R的通信。首先,我已经在Linux Ubuntu上配置好了Rserve环境。本节使用的系统环境是:

  • Linux: Ubuntu 12.04.2 LTS 64bit
  • R: 3.0.1 x86_64-pc-linux-gnu
  • IP: 192.168.1.201

Rserve环境: Rserve v1.7-1,端口: 6311,允许远程访问。

查看Rserve的进程:

~ ps -aux|grep Rserve
conan     9736  0.0  1.2 116288 25440 ?        Ss   13:11   0:01 /usr/lib/R/bin/Rserve --RS-enable-remote

~ netstat -nltp|grep Rserve
tcp        0      0 0.0.0.0:6311            0.0.0.0:*               LISTEN      9736/Rserve

4.4.3 Nodejs配置环境

Nodejs环境也比较简单,以Express3库作为基础的Web框架,rio库是让Nodejs与Rserve通信的依赖库。如果你是Nodejs的新手,需要从头学习,

请参考笔者的博客“从零开始Nodejs系列文章” ( http://blog.fens.me/series-nodejs/ ),如果仅仅搭建一个Nodejs测试环境,请参考“准备Nodejs开发环境Ubuntu ”( http://blog.fens.me/nodejs-enviroment)。

Win7中的开发环境:

  • Node: v0.10.5
  • NPM: 1.2.19
  • IP: 192.168.1.13
  • Express: 3.2.2

现在我们已经在Win7中,创建好了Express3的项目。项目目录:D:\workspace\project\investment\webui ~ D:\workspace\project\investment\webui>ls

README.md app.js models node-rio-dump.bin node_modules package.json public routes views 安装rio库。

~ D:\workspace\project\investment\webui>npm install rio
rio@0.9.0 node_modules\rio
├── hexy@0.2.5
└── binary@0.3.0 (buffers@0.1.1, chainsaw@0.1.0)

4.4.4 Nodejs与R跨平台通信

下面我们开始写Nodejs程序,来实现跨平台的通信。首先在app.js配置文件中,增加一个路由路径。

~ vi app.js
var vis = require('./routes/vis')  //代码有省略
app.get('/vis/rio',vis.rio);

然后在路由目录/routes增加一个路由文件vis.js。

~ vi /routes/vis.js
var rio = require("rio");
exports.rio = function(req, res){
    options = {
        host : "192.168.1.201",  // 远程Rserve服务器地址
        port : 6311,             // 远程Rserve服务器端口
        callback: function (err, val) {  // 计算结果的回调函数
            if (!err) { // 正常返回
                console.log("RETURN:"+val);
                return res.send({'success':true,'res':val}); // 把结果传给界面
            } else {    // 错误返回
                console.log("ERROR:Rserve call failed")
                return res.send({'success':false});
            }
        },
    }
    rio.enableDebug(true);                   //开启调试模式
    rio.evaluate("pi / 2 * 2 * 2",options);  //运行R代码
};

在上面代码中,我们实现了rio与Rserve的远程连接。以字符的形式,把R的语句(pi / 2 2 2)作为参数,传给远程的Rserve服务器,在Rserve服务器中计算,再通过callback的方法,获得R结果的返回值。

打开浏览器( http://localhost:3000/vis/rio ),从Web界面上可以看到(pi / 2 2 2) 计算的结果 是6.283185307179586。在浏览器中计算结构返回值是一个JSON格式的对象。

{
  "success": true,
  "res": 6.283185307179586
}

输出命令行日志。

Connected to Rserve
Supported capabilities --------------

Sending command to Rserve
00000000: 0300 0000 1400 0000 0000 0000 0000 0000  ................
00000010: 0410 0000 7069 202f 2032 202a 2032 202a  ....pi./.2.*.2.*
00000020: 2032 0001         

Data packet
00000000: 2108 0000 182d 4454 fb21 1940            !....-DT{!.@

Type SEXP 33
Response value: 6.283185307179586
RETURN:6.283185307179586
GET /vis/rio 200 33ms - 49b
Disconnected from Rserve
Closed from Rserve

我可以看到Nodejs与Rserve的通信情况,Response value: 6.283185307179586,与页面上面是一致的。 接下来,我们改一下R的运行脚本: rnorm(10),取10个标准正态分布N(0,1)的随机数。

rio.evaluate("rnorm(10)",options);  //运行R代码

浏览器返回的计算结果的JSON对象。

{
  "success": true,
  "res": [
    -0.011531884725262991,
    0.5106443501593562,
    -0.05216533321965309,
    1.9221980152236238,
    0.5205238122633465,
    -0.3275367539102907,
    -0.06588102930129405,
    1.5410418730008988,
    1.308169913050071,
    0.005044179478212583
  ]
}

命令行日志

Connected to Rserve
Supported capabilities --------------

Sending command to Rserve
00000000: 0300 0000 1000 0000 0000 0000 0000 0000  ................
00000010: 040c 0000 726e 6f72 6d28 3130 2900 0101  ....rnorm(10)...

Data packet
00000000: 2150 0000 f6ca 0c5e 079e 87bf 9b4a fad1  !P..vJ.^...?.JzQ
00000010: 3257 e03f eda2 5320 6ab5 aabf 2b25 bdb4  2W`?m"S.j5*?+%=4
00000020: 52c1 fe3f ebba ce8d 21a8 e03f bc17 92b7  RA~?k:N.!(`?<..7
00000030: 5cf6 d4bf ca9f 4642 94dd b0bf 1be3 e485  \vT?J.FB.]0?.cd.
00000040: 1ba8 f83f 5a94 2293 43ee f43f 1724 4e9e  .(x?Z.".Cnt?.$N.
00000050: 34a9 743f                                4)t?

Type SEXP 33
Response value: -0.011531884725262991,0.5106443501593562,-0.05216533321965309,1.9221980152236238,0.5205238122633465,-0.3
275367539102907,-0.06588102930129405,1.5410418730008988,1.308169913050071,0.005044179478212583
RETURN:-0.011531884725262991,0.5106443501593562,-0.05216533321965309,1.9221980152236238,0.5205238122633465,-0.3275367539
102907,-0.06588102930129405,1.5410418730008988,1.308169913050071,0.005044179478212583
GET /vis/rio 200 30ms - 285b
Disconnected from Rserve
Closed from Rserve

我们实现了Nodejs与R的跨平台通信,有点感觉像打通了中国功夫的“任督二脉”,从此以后,成为一代高手。

results matching ""

    No results matching ""