Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous IO server : Thin(Ruby) and Node.js. Any difference?

I wanna clear my concept of asynchronous IO, non-blocking server When dealing with Node.js , it is easy to under the concept

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);
});

I know that when node.js is waiting for 2 seconds of setTimeout, it is able to serve another request at the same time, once the 2 seconds is passed, it will call it callback function.

How about in Ruby world, thin server?

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

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

The code snippet above is using Thin server (non-blocking, asynchronous IO), When talking to asynchronous IO, i want to ask when reaching sleep 2 , is that the server are able to serve another request at the same time as sleep 2 is blocking IO.

The code between node.js and sinatra is that node.js is writing asynchronous way (callback approach) ruby is writing in synchronous way (but working in asynchronous way under the cover? is it true)

If the above statement is true, it seems that ruby is better as the code looks better rather than bunch of callback code in node.js

Kit

like image 995
TheOneTeam Avatar asked Feb 13 '23 06:02

TheOneTeam


1 Answers

Sinatra / Thin

Thin will be started in threaded mode, if it is started by Sinatra (i.e. with ruby asynchtest.rb)

This means that your assumptions are correct; when reaching sleep 2 , the server is able to serve another request at the same time , but on another thread.

I would to show this behavior with a simple test:

#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

let's test it by starting three concurrent http requests ( in here timestamp and thread-id are relevant parts to observe):

enter image description here The test demonstrate that we got three different thread ( one for each cuncurrent request ), namely:

  • 70098572502680
  • 70098572602260
  • 70098572485180

each of them starts concurrently ( the starts is pretty immediate as we can see from the execution of the puts statement ) , then waits (sleeps) ten seconds and after that time flush the response to the client (to the curl process).

deeper understanding

Quoting wikipedia - Asynchronous_I/O: In computer science, asynchronous I/O, or non-blocking I/O is a form of input/output processing that permits other processing to continue before the transmission has finished .

The above test (Sinatra/thin) actually demonstrate that it's possible to start a first request from curl ( the client ) to thin ( the server) and, before we get the response from the first (before the transmission has finished) it's possible to start a second and a third request and these lasts requests aren't queued but starts concurrently the first one or in other words: permits other processing to continue*

Basically this is a confirmation of the @Holger just's comment: sleep blocks the current thread, but not the whole process. That said, in thin, most stuff is handled in the main reactor thread which thus works similar to the one thread available in node.js: if you block it, nothing else scheduled in this thread will run. In thin/eventmachine, you can however defer stuff to other threads.

This linked answers have more details: "is-sinatra-multi-threaded and Single thread still handles concurrency request?

Node.js

To compare the behavoir of the two platform let's run an equivalent asynchtest.js on node.js; as we do in asynchtest.rb to undertand what happen we add a log line when processing starts; here the code of 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);
});

Let's starts three concurrent requests in nodejs and observe the same behavoir:
enter image description here

of course very similar to what we saw in the previous case.

This response doesn't claim to be exhaustive on the subject which is very complex and deserves further study and specific evidence before drawing conclusions for their own purposes.

like image 73
Franco Rondini Avatar answered Feb 15 '23 09:02

Franco Rondini