异步IO服务器:Thin(Ruby)和Node.js。有什么区别吗?

4
我想要澄清异步IO和非阻塞服务器的概念。在处理Node.js时,很容易理解这个概念。
var express = require('express');
var app = express();

app.get('/test', function(req, res){
  setTimeout(function(){
    console.log("sleep doesn't block, and now return");
    res.send('success');
  }, 2000);
});

var server = app.listen(3000, function() {
  console.log('Listening on port %d', server.address().port);
});

我知道当node.js等待2秒的setTimeout时,它可以同时处理另一个请求,在2秒过后,它会调用回调函数。

那么在Ruby世界中,thin服务器如何处理呢?

require 'sinatra'
require 'thin'
set :server, %w[thin]

get '/test' do
  sleep 2   <----
  "success"
end

上面的代码片段使用了Thin服务器(非阻塞,异步IO)。当谈到异步IO时,我想问的是,在达到sleep 2时,服务器是否能够同时为另一个请求提供服务,因为sleep 2是阻塞IO。
在node.js和sinatra之间的代码是这样的: node.js采用异步方式编写(回调方法) ruby以同步方式编写(但在幕后以异步方式工作?这是真的吗)
如果上述说法是正确的, 看起来Ruby更好,因为代码看起来更好,而不是一堆在node.js中的回调代码
Kit
3个回答

9

Sinatra / Thin

Thin将以线程模式启动,如果它是由Sinatra启动的(即使用ruby asynchtest.rb)。

这意味着您的假设是正确的;当到达sleep 2时,服务器能够同时处理另一个请求,但在另一个线程上。

我想用一个简单的测试来展示这种行为:

#asynchtest.rb
require 'sinatra'
require 'thin'
set :server, %w[thin]

get '/test' do
  puts "[#{Time.now.strftime("%H:%M:%S")}] logging /test starts on thread_id:#{Thread.current.object_id} \n"
  sleep 10
  "[#{Time.now.strftime("%H:%M:%S")}] success - id:#{Thread.current.object_id} \n"
end

让我们通过同时发起三个http请求来测试它(在这里,时间戳线程ID是需要观察的相关部分):

enter image description here 测试表明我们得到了三个不同的线程(每个并发请求一个),即:

  • 70098572502680
  • 70098572602260
  • 70098572485180

它们每个都同时开始(正如我们可以从puts语句的执行中看到的那样),然后等待(休眠)十秒钟,在那段时间之后将响应刷新到客户端(curl进程)。

更深入的理解

引用维基百科的一句话:异步I/O或非阻塞I/O是一种输入/输出处理方式,允许在传输完成之前进行其他处理。

上面的测试(Sinatra/thin)实际上证明了可以从curl(客户端)向thin(服务器)发起第一个请求,并且在我们获得第一个响应之前(在传输完成之前)可以启动第二个和第三个请求,这些最后的请求不会排队,而是同时启动第一个请求,或者换句话说:允许其他处理继续运行*

基本上,这是对@Holger的评论的证实:sleep会阻塞当前线程,但不会阻塞整个进程。也就是说,在thin中,大部分工作都在主反应器线程中处理,因此它的工作方式类似于node.js中可用的一个线程:如果你阻塞了它,那么在该线程中安排的其他所有任务都将停止运行。在thin/eventmachine中,你可以将东西推迟到其他线程。

这些链接的答案有更多细节:“is-sinatra-multi-threaded”和“Single thread still handles concurrency request?

Node.js

为了比较这两个平台的行为,让我们在node.js上运行一个等效的asynchtest.js;正如我们在asynchtest.rb中所做的那样,为了理解发生了什么,我们在处理开始时添加了一条日志记录行;这里是asynchtest.rb的代码:

var express = require('express');
var app = express();

app.get('/test', function(req, res){
  console.log("[" + getTime() + "] logging /test starts\n");
  setTimeout(function(){
    console.log("sleep doen't block, and now return");
    res.send('[' + getTime() + '] success \n');
  },10000);
});

var server = app.listen(3000,function(){
  console.log("listening on port %d", server.address().port);
});

让我们在Node.js中启动三个并发请求并观察相同的行为:
enter image description here

当然,与我们在先前情况下看到的非常相似。

这个响应并不打算在这个非常复杂的主题上详尽无遗,它值得进一步研究和具体证据,以便在自己的目的之前得出结论。


谢谢你的回答。我只是想知道,如果thin以线程模式启动,那么我们就无法享受到thin的异步非阻塞IO行为的好处了。众所周知,非阻塞模式的好处在于,如果操作被阻塞,我们可以在此期间处理任何其他请求... - TheOneTeam
优化了答案并添加了链接有关thin和Non blocking IOs/Reactor pattern的更多细节,仔细检查两个测试的截图可能会有所帮助。 - Franco Rondini

0

有很多微妙的差别,几乎无法在此列出。

首先,不要将“编码风格”与“事件模型”混淆。在Node.js中使用回调函数没有必要(可以看看各种“promise”库)。如果喜欢回调结构化代码,Ruby有EventMachine

其次,Thin(和Ruby)可以有许多不同的多任务模型。您没有指定哪一个。

  • 在Ruby 1.8.7中,"Thread"将创建green threads。这种语言实际上将"sleep N"转换为一个计时器调用,并允许其他语句执行。但它有很多限制。
  • Ruby 1.9.x可以创建本地操作系统线程。但这些线程使用起来可能很困难(启动成千上万个对性能不利等)。
  • Ruby 1.9.x具有"Fibers",这是一个更好的抽象,非常类似于Node。

在任何比较中,您还必须考虑整个生态系统:几乎任何Node.js代码都可以在回调函数中运行。编写阻塞代码非常困难。但许多Ruby库默认情况下都不支持线程(需要特殊配置等)。许多看似简单的事情(例如DNS)可能会阻塞整个Ruby进程。

你还需要考虑编程语言。Node.JS是基于JavaScript构建的,它有很多容易让你犯错的地方。例如,很容易假设JavaScript有整数,但实际上它没有。Ruby则有较少的难点(如元编程)。

如果你真的喜欢事件驱动架构,那么你应该考虑Go。它拥有最好的所有优点:事件驱动架构内置(就像在Node中一样,只不过它支持多处理器),没有回调(就像在Ruby中一样),而且它具有一流的消息传递功能(非常类似于Erlang)。作为奖励,它将使用比Node或Ruby进程更少的内存。


即使使用了 Promise,在 Node 中仍然需要使用回调函数。 - lucaswxp

-1

不,Node.js 是完全异步的,setTimeout 不会阻塞脚本执行,只会延迟其中的一部分。因此,这些代码部分并不相等。选择适合您项目的平台取决于您想要达到的任务。


在 Ruby / Thin 的世界中呢?调用 sleep 时,它会阻塞脚本还是可以同时处理另一个请求呢? - TheOneTeam
@KitHo 是的,Ruby的sleep会暂停整个进程。 - monkeyinsight
我不这么认为,如果是的话,Thin可以处理多少并发请求? - TheOneTeam
@monkeyinsight,这不是真的。sleep 阻塞当前线程,但并不会阻塞整个进程。话虽如此,在 thin 中,大多数东西都在主反应器线程中处理,因此它类似于 node.js 中可用的一个线程:如果您阻塞它,则此线程中 安排的其他任务 将无法运行。 但是,在 thin/eventmachine 中,您可以将任务延迟到其他线程中执行。 - Holger Just
@就是这样,如果使用thin,当代码执行 sleep 2 时,它们能像Node.js一样服务于另一个请求吗?(因为它是非阻塞IO) - TheOneTeam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接