22 de jul. de 2011

Primeiro contato com Node.j e mais sobre non-blocking servers



Dae gurizada..

Na sequência do meus estudos sobre non-blocking web servers vou dar uma olhada no Node.js.


Vou utilizar como base esta apresentação do Ryan Dahl, que está disponível no site do node.js

Antes de olhar sobre Node.js é importante entender o C10K Problem e Non-blocking I/O

Node.js
Node.js nada mais é que uma biblioteca que roda em cima do virutal machine chamada v8. V8 é uma JavaScript Engine escrita pelos  engenheiros da Google e é utilizada no Google Chrome.

Node utiliza o v8 para fazer o trabalho de comunicação (networking) corretamente. 

Não existe "waiting time"
Óbvio, é isso que faze dele um non-blocking server: Não ter "waiting time".

Você não consegue mandar o node dormir (sleep). Não é que você seja encorajado a não fazer um "sleep", na verdade você não tem poder para isso. 

O que o Node faz é definir Timeouts e deixar processos em idle (ocioso), fazendo com que o consumo de CPU vá para zero.

De uma olhada nesse código:



 setTimeout( function(){
    console.log("world");
   }, 2000
 )

 console.log("hello");

A função timeout do java script está recebendo dois argumentos:
  1. Uma função callback que será executada depois do tempo de espera
  2. o tempo de espera em milisegundos
//output
hello
world
O que acontece aqui é que o programa não para quando chamo "setTimeout".  O programa segue o o código que está dento da função passada para o "setTime" fica em idle e espera 2 segundos para ser executado.

Node nunca pára!

O script não termina quando chega ao fim
Mudei o código e chamei a função setInterval() ao invés da setTimeout(). Essa função executa a função recebida como parâmetro infinitamente a cada X milesegundos, definido no segundo argumento.

 setInterval( function(){
    console.log("world");
   }, 2000
 )

 console.log("hello");
O resultado foi:

hello
world
world
world
world
world
world

Note que o script chegou ao fim, ou seja, executou a última linha de comando. Mas o programa continou sendo executando devido a chamada da função  setInterval().

O lado bom disso é que você pode escrever tudo que precisa ser feito e não precisa se preocupar como manter o processo ativo.

Um ctrl + C resolve a parada.

Simples web server

Abaixo está um exemplo de como criar um servidor http. Esse é o mesmo código que está no site do Node.

var http = require('http');

var server = http.createServer(
  function(request, response){
     response.writeHead(200, {'content-type':'text/plain'});
     response.end("Hello Wrold\n");
  } 
);

server.listen(8080);
console.log("Server running at http://localhost: 8080");


A primeira linha carrega o pacote http. Depois disso criamos o serviço e definos a função que irá tratar cada requisição ao servidor.

Para iniciar o servidor basta digitar "node test3-httpServer.js" e  será iniciado.

Fiz uma requisição utilizando o comando "curl" com o parâmetro "-i" para retornar o cabeçalho HTTP.

simundi@simundi ~ $ curl -i http://localhost:8080
HTTP/1.1 200 OK
content-type: text/plain
Connection: keep-alive
Transfer-Encoding: chunked

Hello Wrold


Analisando o cabeçalho temos:

  • Versão 1.1:  a última versãõ HTTP
  • Código 200: significa que a requisção foi bem sucedida.
  • Connection keep-alive: vai fazer com que a conexão TCP não seja enerrada logo após a reposta. O que é bom. Afinal, criar conexão TCP toda hora é um custo desnecessário.
  • Transfer-encoding Chunked: esse vale a pena explicar bem

Transfer-encoding: Chunked / Resposta Assíncrona

Antes de entender este atributo, vamos ver como as coisas funcionam sem ele. Um dos atributos existentes no cabeçalho HTTP é o Content-lenght, que diz o tamnho da reposta que está chegando do servidor. Dessa forma o browser(ou outro cliente) sabe quando a reposta terminou de ser enviada.

Reposta com Content-Length in the header.

Com o "Transfer-enconding" definido como "Chunked" NÃO TEM Content-Length. Ou seja, para o browser a resposta vai continuar chegando para sempre, até que o servidor informe que a resposta acabou.


Enviando a resposta é enviada em "prestações"

Agora vou mostar um exemplo disso no node.

var http = require('http')

var server = http.createServer(
  function(request, response){
     response.writeHead(200, {'content-type':'text/plain'});
     response.write("Hello world\n")
     setTimeout( function() {
         response.end("Second answer\n")
     },2000 );

    response.write("end of the script\n")

  } 
);

server.listen(8080);
console.log("Server running at http://localhost: 8080");

Output:
Hello world
end of the script
later response

Nesse exemplo o programa escreve "hello world" e "end of script" direto. Depois de 2 segundos ele escreve "later response".  Note que é código "response.end" que informa ao cliente que a resposta acabou. Isso é o que o "Transfer-Coding: Chunked" faz.


Outra coisa importante que de se notar é que entre a resposta "end of the script" e "later response" o servidor não parou. Ele continua rodando e disponível para receber novas requisições.

Código dos exemplos: https://github.com/simundi/gyro/tree/master/lab/nodejs


Por hoje é só. Node tem várias outras funcionalidades, mas estas vão ficar para outra hora.

Nenhum comentário: