Rails 和 node.js 各有所长,Rails 在快速原型实现速度上远远比相对年轻的 node.js 快很多。大量的第三方库使得写 Rails 程序非常简单。但是在大量链接情况和实时应用的情况下编写 node.js 相对简单,尤其是有 socket.io/engine.io/sock.js 这些逆天的跨平台实时库。
在 Rails 2/3 的时期,如果不是大量使用客户端 Javascript,页面切换会较为频繁,使得 Socket.io 这种实时前端结构不是很好集成 - 每次页面切换都会造成 socket 断开重连。
Rails 4 引入了 Turbolinks,这个特性(库)有可能造成前端对象、内存泄漏,但是同样的,这个 Turbolinks 使得页面不再切换,socket.io 这种库不需要在页面跳转的时候重新连接了。
我刚刚做了一点小小的实验,完成了一个 Rails 4 + Turbolinks + socket.io 的原型,请大家提提意见。实现的功能很简单,/pages/create动作可以发送广播信息,/pages/{new,recv}接收广播信息,并且在 console 打出。
Rails 处理基本页面,使用 node.js 的 express 模块实现了一个反向代理服务器,为 Rails 提供代理。
结构如下
浏览器  <====> node.js (express + socket.io) <====> Rails
node.js 代码
var express = require('express');
var app = express();
var server = app.listen(3001);
var request = require('request');
var io = require('socket.io').listen(server);
app.use(express.bodyParser());
app.use('/broadcast', function (req, res) {
    var data = req.body.data;
    res.end();
    io.sockets.emit('broadcast', data);
});
app.use('/', function(req, res) {
    var url = "http://127.0.0.1:3000" + req.url;
    var r = null;
    
    if (req.method == 'POST') {
        r = request.post({uri: url, json: req.body});
    } else {
        r = request(url);
    }
    req.pipe(r).pipe(res);
});
在application.html.erb,</head>之前加入 socket.io 相关代码
<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect();
  socket.on('broadcast', function (data) {
    console.log(data);
  });
</script>
最后,实现pages_controller.rb的create 
def create
    content = params[:content]
    Net::HTTP.post_form(URI('127.0.0.1:3001/broadcast'),
                          "data" => content,
                        )
    redirect_to :action => :recv 
end
测试打开两个窗口,一个窗口在 pages/recv,一个窗口发送消息。最好的是接受窗口由于 Turbolinks 的作用,随意点击链接跳转都不会让 socket.io 重新链接。