O JavaScript tornou-se, nos últimos anos, o herói subestimado do desenvolvimento de aplicativos da web. Esse reconhecimento vem sendo encarado com surpresa por desenvolvedores de software acostumados a considerar JavaScript uma “linguagem de brinquedo”. Embora haja linguagens mais populares (no sentido de que desenvolvedores clamam sua lealdade a elas), o status exclusivo de JavaScript como a linguagem de script padrão, independente de navegador, fez com que ela pudesse perdurar. Para desenvolvimento na web no lado do cliente, é provavelmente a linguagem mais usada no mundo.
JavaScript também tem lugar em scripts do lado do servidor, e esse nicho está crescendo. Embora tenha havido tentativas de JavaScript no lado do servidor no passado, nenhuma gerou tanta animação quanto Node.js, ou Node.
Projetado para auxiliar desenvolvedores a criar programas de rede escaláveis, o Node é um ambiente de programação do lado do servidor que praticamente reinventou o JavaScript para uma nova geração de desenvolvedores. Para muitos desenvolvedores Java, a principal atração do Node é sua abordagem nova para a simultaneidade de software. Embora a plataforma Java continue evoluindo em sua abordagem da simultaneidade (com melhorias muito esperadas em Java 7 e Java 8), o fato é que Node atende uma necessidade, e faz isso da forma mais leve que muitos desenvolvedores Java adotaram recentemente. Como os scripts do lado do cliente com JavaScript, os que estão do lado do servidor no ambiente Node são bons porque funcionam, e funcionam em uma área na qual muitos desenvolvedores Java enfrentam desafios atualmente.
Neste artigo, eu apresento a revolução dos scripts do lado do servidor que é o Node. Começaremos com uma visão geral arquitetural do que torna o Node especial, e em seguida eu mostrarei como desenvolver rapidamente um aplicativo da web escalável que usa MongoDB para persistência de dados. Você verá em primeira mão como o Node é divertido, e como ele pode ser usado rapidamente para montar um aplicativo da web funcional.
A simultaneidade conduzida por evento de Node
Node é um ambiente de E/S escalável, conduzido por evento, desenvolvido com base no mecanismo JavaScript V8 do Google. O Google V8, na verdade, compila JavaScript em código de máquina nativo antes da execução, o que resulta em um desempenho extremamente rápido no tempo de execução — algo que não é geralmente associado a JavaScript. Dessa forma, o Node permite desenvolver rapidamente aplicativos de rede extremamente rápidos e altamente simultâneos.
E/S conduzida por evento pode soar estranho para um desenvolvedor Java, mas não é tão novo assim. Em vez do modelo de programação multiencadeado, com o qual estamos acostumados na plataforma Java, a abordagem de Node para simultaneidade tem um único encadeamento, com a adição de um loop de eventos. Esse desenvolvimento do Node permite E/S sem bloqueio ou assíncrona. No Node, chamadas que geralmente bloqueariam, como espera por resultados de consulta de banco de dados, não bloqueiam. Em vez de esperar que atividades caras de E/S sejam concluídas, um aplicativo Node emite um retorno de chamada. Quando um recurso é retornado, o retorno de chamada anexado é feito de forma assíncrona.
A simultaneidade simplesmente funciona em programas Node. Se eu quisesse executar o cenário anterior na plataforma Java, poderia escolher entre abordagens complexas e demoradas — de encadeamentos tradicionais a bibliotecas mais novas em Java NIO e até o pacote java.util.concurrent melhorado e atualizado. Embora a simultaneidade de Java seja eficiente, pode ser difícil de entender — o que se traduz em código! Em comparação, o mecanismo de retorno de chamada do Node está integrado na linguagem; não são necessários desenvolvimentos especiais como synchronized para fazê-la funcionar. O modelo de simultaneidade do Node é extremamente simples, e isso o torna acessível para um público maior de desenvolvedores.
A sintaxe de JavaScript do Node também economiza bastante digitação. Com pouco código é possível desenvolver um aplicativo da web rápido e escalável, capaz de lidar com diversas conexões simultâneas. Isso pode ser feito na plataforma Java, mas é necessário mais linhas de código e diversas bibliotecas e desenvolvimentos adicionais. E não precisa se preocupar em navegar um ambiente de programação novo: oNode é fácil de aprender se você sabe um pouco de JavaScript, e eu aposto que você sabe.
Introdução ao Node
Como eu falei, o Node é fácil de aprender e você encontrará diversos bons tutoriais on-line para ajudá-lo. Neste artigo meu foco é ajudar desenvolvedores Java a aproveitar os benefícios do Node. Em vez de examinar o aplicativo de servidor da web “Hello, world” padrão, quero passar direto para um aplicativo útil, que realmente faz algo: pense nele como uma espécie de clone do Foursquare desenvolvido em Node.
Instalar o Node requer seguir as instruções para sua plataforma em particular; caso esteja usando uma plataforma semelhante a UNIX, como OSX, recomendo usar Node Version Manager, ou NVM, que lida com os detalhes da instalação de uma versão apropriada do Node. Em todo caso, faça download do Node e instale-o agora.
Também usaremos algumas bibliotecas de terceiros para desenvolver este aplicativo, portanto instale NPM, que é o gerenciador de pacote do Node. O NPM permite especificar dependências com versão para seu projeto, que são depois transferidas por download e incluídas no seu caminho de desenvolvimento. Ele é muito semelhante ao Maven para a plataforma Java, ou ao Bundler do Ruby.
Node Express
O Node está se tornando popular entre desenvolvedores da web devido à sua capacidade de lidar com simultaneidade e porque foi desenvolvido com o desenvolvimento para a web em mente. Uma das mais populares ferramentas de terceiros do Node é o Express, uma estrutura leve de desenvolvimento da web, que usaremos para desenvolver nosso aplicativo. Consulte “Recursos” para saber mais sobre o Express.
O Express está carregado de recursos, incluindo roteamento sofisticado, visualizações de modelo dinâmicas (veja a estrutura Node du jour: Jade) e negociação de conteúdo. Ele também é muito leve, sem ORM integrado ou bagagem similar para pesar. Nesse sentido, não se compara a Rails, Grails ou qualquer outra estrutura da web completa.
A maneira mais fácil de instalar e usar o Express é declará-lo como uma dependência por meio do arquivo package.json do NPM, mostrado na Listagem 1. Esse arquivo é semelhante ao pom.xml do Maven ou ao Gemfile do Bundler, mas seu formato é JSON.
{
"name": "magnus-server",
"version": "0.0.1",
"dependencies": {
"express": "2.4.6"
}
}
Listagem 1.
Na Listagem 1, eu dei a este projeto do Node um nome (magnus-server) e uma versão (0.0.1). Também declarei que a versão 2.4.6 do Express é uma dependência. O bom do NPM é que ele irá obter todas as dependências transitivas do Express, carregando rapidamente qualquer outra biblioteca de terceiros do Node que o Express necessite.
Após definir as dependências do projeto por meio de package.json, é possível instalar os pacotes desejados por meio da linha de comando, digitando npm install. Você deverá ver o NPM instalar o Express junto com dependências como connect, mime e outras.
Criando um aplicativo de rede
Para começar a escrever nosso aplicativo de exemplo, Magnus Server, criamos um arquivo JavaScript; eu chamei o meu de web.js, mas você pode chamá-lo do que quiser. Abra esse arquivo em seu editor ou IDE favorito; por exemplo, você pode usar JSDT, o plugin de JavaScript do Eclipse – consulte “Recursos”.
No arquivo, inclua o código na Listagem 2:
var express = require('express');
var app = express.createServer(express.logger());
app.put('/', function(req, res) {
res.contentType('json');
res.send(JSON.stringify({ status: "success" }));
});
var port = process.env.PORT || 3000;
app.listen(port, function() {
console.log("Listening on " + port);
});
Listagem 2.
Primeiro, se quisermos usar uma biblioteca de terceiros no Node, precisamos usar a frase require; na Listagem 2, estamos exigindo a estrutura Express, e também estamos obtendo uma manipulação por meio da variável express. Em seguida, criamos uma instância de aplicativo por meio da chamada createServer, que por sua vez, cria um servidor HTTP.
Em seguida, definimos um terminal por meio de app.put. Nesse caso, estamos definindo um PUT de HTTP como o método HTTP necessário ouvindo na raiz do aplicativo (/). A chamada put tem dois parâmetros: a rota e um retorno de chamada correspondente a ser executado quando a rota é chamada. O segundo parâmetro é uma função que é chamada no tempo de execução quando o terminal / é atingido. Lembre-se, esse retorno de chamada é o que queremos dizer quando falamos na E/S conduzida por evento ou eventualizada do Node — o retorno de chamada será chamado de forma assíncrona. Esse terminal pode lidar com diversas solicitações simultâneas sem a necessidade de criar encadeamentos manualmente.
Como parte da definição do terminal, nós criamos a lógica para lidar com um PUT para /. Para manter as coisas simples por enquanto, iremos definir o tipo de resposta como JSON e enviar de volta um documento JSON simples: ({“status”:”success”}). Observe o elegante método stringify, que toma um hash e o converte em JSON.
Em seguida, criamos uma variável representando a porta na qual o aplicativo deve ouvir; para isso, obtemos a variável de ambiente PORT, ou a definimos explicitamente como 3000. Por fim, iniciamos o aplicativo chamando o método listen. Novamente nós passamos um retorno de chamada, que será chamado caso o aplicativo esteja executando para imprimir uma mensagem no console (nesse caso, saída padrão).
Experimente!
Esse elegante aplicativo responde a qualquer PUT, portanto é possível executá-lo digitando simplesmente node web.js na linha de comando. Caso deseje testar melhor o aplicativo, recomendo fazer download do RESTClient de WizTools.org. Com RESTClient, é possível descobrir rapidamente se o Magnus Server funciona ao executar um PUT de HTTP para http://localhost:3000. Se tudo der certo, você deve ver uma resposta de JSON indicando sucesso. Consulte “Recursos” para saber mais sobre a instalação e o uso do RESTClient.
Express cuida de JSON
O JavaScript e o JSON são parentes próximos, o que torna o gerenciamento de Json no Express o mais simples possível. Nesta seção, iremos incluir um pouco mais de código no aplicativo esqueleto da Listagem 2, para que possamos obter um documento JSON recebido e imprimi-lo na saída padrão. Após isso, iremos persistir tudo em uma instância do MongoDB.
O documento recebido será semelhante à Listagem 3 (observe que eu omiti informações de local devido ao espaço):
{
"deal_description":"free food at Freddie Fingers",
"all_tags":"free,burgers,fries"
}
Listagem 3.
A Listagem 4 inclui funcionalidade para analisar o documento recebido:
app.use(express.bodyParser());
app.put('/', function(req, res) {
var deal = req.body.deal_description;
var tags = req.body.all_tags;
console.log("deal is : " + deal + " and tags are " + tags);
res.contentType('json');
res.send(JSON.stringify({ status: "success" }));
});
Listagem 4.
Observe que a Listagem 4 inclui uma linha que orienta o Express a usar bodyParser. Isso nos permitirá facilmente (e eu realmente quero dizer facilmente) obter atributos de um documento JSON recebido.
Dentro do retorno de chamada put há código para obter os valores dos atributos deal_description e all_tags de um documento recebido. Observe a facilidade com que podemos capturar os elementos individuais de um documento solicitado: neste caso, req.body.deal_description obtém o valor de deal_description.
Teste!
Também é possível testar essa implementação. Basta parar sua instância do magnus-server e reiniciá-la, e em seguida usar um PUT de HTTP para enviar um documento JSON para o seu aplicativo Express. Primeiro, você deve ver uma resposta de sucesso. Em seguida, o Express deve registrar na saída padrão os valores enviados. Com meu documento de Freddie Fingers, eu recebo esta saída:
deal is : free food at Freddie Fingers and tags are free, burgers, fries.
Persistência com Node
Temos um aplicativo funcional que recebe um documento JSON, analisa-o e retorna uma resposta. Agora precisamos incluir alguma lógica de persistência. Como sou fã do MongoDB – consulte “Recursos”, eu optei por persistir os dados por meio de uma instância do MongoDB. Para tornar as coisas ainda mais vivas, iremos usar a biblioteca de terceiros, Mongolian DeadBeef, que usaremos para armazenar os valores de documentos JSON recebidos.
O Mongolian DeadBeef é uma de várias bibliotecas do MongoDB disponíveis para Node. Eu a escolhi porque o nome me agrada, e porque o espelhamento do driver MongoDB nativo me faz sentir em casa. Agora você já sabe que a primeira etapa para usar o Mongolian DeadBeef é atualizar o arquivo package.json, como mostra a Listagem 5:
{
"name": "magnus-server",
"version": "0.0.1",
"dependencies": {
"express": "2.4.6",
"mongolian": "0.1.12"
}
}
Listagem 5.
Como estamos conectando com um armazenamento de dados do MongoDB, também é preciso atualizar as dependências sólidas do projeto executando npm install. Para melhorar o desempenho do driver do MongoDB do Mongolian DeadBeef, poderíamos, também, instalar o analisador bson C++ nativo, algo no qual o NPM pode nos ajudar.
Para começar a usar o Mongolian DeadBeef, incluímos outro require na implementação atual, e em seguida conectamos a instância MongoDB desejada (como mostra a Listagem 6). Para esse exemplo, iremos conectar a uma instância hospedada no MongoHQ, um provedor de nuvem do MongoDB.
var mongolian = require("mongolian");
var db = new mongolian("mongo://a_username:a_password@flume.mongohq.com:23034/magnus");
Listagem 6.
Dentro do retorno de chamada PUT, persistimos os valores do documento JSON recebido, como mostra a Listagem 7:
app.put('/', function(req, res) {
var deal = req.body.deal_description;
var tags = req.body.all_tags;
db.collection("deals").insert({
deal: deal,
deal_tags: tags.split(",")
})
res.contentType('json');
res.send(JSON.stringify({ status: "success" }));
});
Listagem 7.
Examinando de perto, você verá que a instrução insert é idêntica a uma inserção dentro do shell do MongoDB. Isso não é coincidência – o shell do MongoDB usa JavaScript! Como consequência, podemos persistir facilmente um documento que tenha dois campos: deal e deal_tags. Observe que definimos deal_tags como um array, usando o método split na cadeia de caracteres tags.
Posso começar? (Sim, pode!)
Caso deseje fazer um teste (e quem não iria querer?), reinicie sua instância, envie outro documento JSON e verifique sua coleção de deals no MongoDB. Você deve ver um documento JSON quase idêntico àquele que você enviou.
{
deal:"free food at Freddie Fingers",
deal_tags: ["free", "burgers", "fries"],
_id: "4e73ff3a41258b7423000001"
}
Listagem 8.
Em conclusão: é isso!
Se você acha que estou sendo preguiçoso por acabar tão cedo esta pequena introdução ao Node.js, tenho uma surpresa para você: já concluímos! Escrevemos apenas umas 20 linhas de código, mas elas representam um aplicativo completo e persistente – e essa é a beleza do Node. É muito simples de escrever e entender, e seus retornos de chamada assíncronos o tornam extremamente eficiente. Quando um aplicativo é escrito, pode ser implementado em qualquer número de provedores PaaS, para escalabilidade máxima.
Consulte a seção “Recursos” para aprender mais sobre as tecnologias discutidas neste artigo, incluindo Node.js, MongoDB e opções de PaaS como Google App Engine, Elastic Beanstalk da Amazon e Heroku.
Recursos
Aprender
- Desenvolvimento Java em 2.0: Explore as tecnologias que estão redefinindo o cenário de desenvolvimento Java, incluindo: JavaScript para Desenvolvedores Java (Abril de 2011), Kilim (abril de 2010), RESTClient (novembro de 2009), Gretty (agosto de 2011) e MongoDB (setembro de 2010).
- “O que exatamente é o Node.js?” (Michael Abernethy, developerWorks, maio de 2011): Saiba mais sobre Node, um interpretador JavaScript do lado do servidor que revolucionou ideias comuns sobre como um servidor deve operar.
- “Use o Node.js como uma Pilha Completa de Desenvolvimento de Ambiente de Nuvem” (Noah Gift e Jeremy Jones, developerWorks, abril de 2011): Discute a estrutura e ecossistema do Node, e apresenta um exercício para usar o Node para desenvolver um servidor de bate-papo.
- Caminho de conhecimento de simultaneidade Java: Um recurso do developerWorks para desenvolvedores de software que desejam dominar a programação multiencadeada na plataforma Java, bem como alternar abordagens que exploram hardware de processador com diversos núcleos.
- Caminho de conhecimento: Fundamentos da computação em nuvem: Apresenta conceitos de computação em nuvem e os modelos de serviço IaaS, PaaS e SaaS.
- Zona tecnologia Java do developerWorks: Encontre centenas de artigos sobre quase todos os aspectos da programação Java.
- Navegue pela livraria da tecnologia Java para ver livros sobre este e outros tópicos técnicos.
Obter produtos e tecnologias
- Saiba mais sobre as tecnologias discutidas neste artigo:
Discutir
Participe da comunidade do My developerWorks: Entre em contato com outros usuários do developerWorks e explore os blogs, fóruns, grupos e wikis voltados para desenvolvedores.
***
Sobre o autor: Andrew Glover é desenvolvedor, autor, palestrante e empreendedor com uma paixão por desenvolvimento direcionado por comportamento, integração contínua e desenvolvimento de software Agile. Ele é o fundador da easyb , estrutura Behavior-Driven Development (BDD) e é coautor de três livros: Continuous Integration, Groovy in Action, e Java Testing Patterns. Você pode acompanhá-lo em seu blog e seguindo-o no Twitter.