Uso o Node.js no trabalho e considero muito poderoso. Forçado a escolher uma palavra para descrever o Node.js, eu diria "interessante" (que não é um adjetivo puramente positivo). A comunidade é vibrante e crescente. O JavaScript, apesar de suas peculiaridades, pode ser uma ótima linguagem para codificar. E você repensará diariamente sua própria compreensão das "melhores práticas" e dos padrões de código bem estruturado. Há uma enorme energia de idéias fluindo para o Node.js agora, e trabalhar nele expõe você a todo esse pensamento - excelente levantamento de peso mental.
O Node.js em produção é definitivamente possível, mas longe da implantação "pronta", aparentemente prometida pela documentação. Com o Node.js v0.6.x, o "cluster" foi integrado à plataforma, fornecendo um dos componentes essenciais, mas meu script "production.js" ainda possui ~ 150 linhas de lógica para lidar com coisas como criar o log diretório, reciclagem de trabalhadores mortos, etc. Para um serviço de produção "sério", você também precisa estar preparado para limitar as conexões de entrada e fazer tudo o que o Apache faz para PHP . Para ser justo, o Ruby on Rails tem esse problema exato . É resolvido através de dois mecanismos complementares: 1) Colocando Ruby nos Rails / Node.Apache / Lighttd ). O servidor da web pode servir com eficiência conteúdo estático, acessar log, reescrever URLs, encerrar SSL , impor regras de acesso e gerenciar vários sub-serviços. Para solicitações que atingem o serviço do nó real, o servidor da web realiza proxy da solicitação. 2) Usando uma estrutura como o Unicorn, que gerenciará os processos de trabalho, reciclá-los periodicamente, etc. Ainda não encontrei uma estrutura de serviço do Node.j que pareça totalmente preparada; pode existir, mas ainda não o encontrei e ainda uso ~ 150 linhas no meu "production.js" enrolado à mão.
Estruturas de leitura como o Express fazem parecer que a prática padrão é servir apenas tudo através de um serviço Node.js. de todos os tipos ... "app.use (express.static (__ dirname + '/ public'))" . Para serviços e desenvolvimento de carga menor, provavelmente isso é bom. Porém, assim que você tentar colocar um carregamento pesado em seu serviço e executá-lo 24 horas por dia, sete dias por semana, você descobrirá rapidamente as motivações que levam os sites a criar códigos C bem endurecidos e endurecidos, como o Nginx, na frente do site e na manipulação de todos os sites. das solicitações de conteúdo estático (... até você configurar uma CDN , como o Amazon CloudFront )). Para uma opinião um tanto humorística e descaradamente negativa, veja esse cara .
O Node.js também está encontrando cada vez mais usos não relacionados a serviços. Mesmo se você estiver usando outra coisa para veicular conteúdo da Web, ainda poderá usar o Node.js como uma ferramenta de construção, usando módulos npm para organizar seu código, Browserify para costurá-lo em um único ativo e uglify-js para minimizá- lo para implantação . Para lidar com a web, o JavaScript é uma correspondência perfeita de impedância e freqüentemente a torna a rota de ataque mais fácil. Por exemplo, se você deseja analisar várias cargas úteis de resposta JSON , deve usar meu módulo CLI de sublinhado , o cinto de utilidades dos dados estruturados.
Prós e contras:
- Profissional: para um servidor, escrever JavaScript no back-end tem sido uma "droga de gateway" para aprender padrões modernos de interface do usuário. Não tenho mais medo de escrever o código do cliente.
- Pro: tende a incentivar a verificação adequada de erros (o erro é retornado por praticamente todos os retornos de chamada, incomodando o programador; também, async.js e outras bibliotecas lidam com o paradigma "falhar se alguma dessas subtarefas falhar" muito melhor do que o código síncrono típico )
- Pro: algumas tarefas interessantes e normalmente difíceis tornam-se triviais - como obter status das tarefas em andamento, comunicar-se entre trabalhadores ou compartilhar o estado do cache
- Pro: comunidade enorme e toneladas de ótimas bibliotecas baseadas em um sólido gerenciador de pacotes (npm)
- Con: JavaScript não tem biblioteca padrão. Você se acostuma a importar funcionalidades que parecem estranhas quando você usa o JSON.parse ou algum outro método de compilação que não requer a adição de um módulo npm. Isso significa que existem cinco versões de tudo. Até os módulos incluídos no "núcleo" do Node.js. possuem mais cinco variantes, caso você não esteja satisfeito com a implementação padrão. Isso leva a uma evolução rápida, mas também a um certo nível de confusão.
Versus um modelo simples de um processo por solicitação ( LAMP ):
- Pro: escalável para milhares de conexões ativas. Muito rápido e muito eficiente. Para uma frota da web, isso pode significar uma redução de 10 vezes o número de caixas necessárias em comparação com PHP ou Ruby
- Pro: Escrever padrões paralelos é fácil. Imagine que você precisa buscar três (ou N) blobs do Memcached . Faça isso no PHP ... você acabou de escrever o código que busca o primeiro blob, depois o segundo e o terceiro? Uau, isso é lento. Há um módulo PECL especial para corrigir esse problema específico do Memcached, mas e se você quiser buscar alguns dados do Memcached em paralelo com a sua consulta ao banco de dados? No Node.js, como o paradigma é assíncrono, é muito natural ter uma solicitação da Web para fazer várias coisas em paralelo.
- Contras: O código assíncrono é fundamentalmente mais complexo que o código síncrono, e a curva de aprendizado inicial pode ser difícil para os desenvolvedores sem uma compreensão sólida do que a execução simultânea realmente significa. Ainda assim, é muito menos difícil do que escrever qualquer tipo de código multithread com bloqueio.
- Con: Se uma solicitação de uso intensivo de computação for executada por, por exemplo, 100 ms, interromperá o processamento de outras solicitações que estão sendo tratadas no mesmo processo Node.js. ... AKA, multitarefa cooperativa . Isso pode ser mitigado com o padrão Web Workers (desmembramento de um subprocesso para lidar com a tarefa cara). Como alternativa, você pode usar um grande número de trabalhadores do Node.js. e apenas permitir que cada um lide com uma única solicitação simultaneamente (ainda bastante eficiente porque não há reciclagem do processo).
- Contras: A execução de um sistema de produção é MUITO mais complicada do que um modelo CGI como Apache + PHP, Perl , Ruby , etc. Exceções não tratadas derrubarão todo o processo, exigindo lógica para reiniciar os trabalhadores com falha (consulte o cluster ). Módulos com código nativo de buggy podem travar o processo com dificuldade. Sempre que um trabalhador morre, todas as solicitações que ele estava manipulando são descartadas; portanto, uma API de buggy pode degradar facilmente o serviço para outras APIs hospedadas.
Contra escrever um serviço "real" em Java / C # / C (C? Sério?)
- Pro: Fazer assíncrono no Node.js é mais fácil do que fazer segurança de encadeamento em qualquer outro lugar e, sem dúvida, oferece maiores benefícios. O Node.js é de longe o paradigma assíncrono menos doloroso em que já trabalhei. Com boas bibliotecas, é apenas um pouco mais difícil do que escrever código síncrono.
- Pro: sem erros de multithreading / locking. É verdade que você investe com antecedência escrevendo códigos mais detalhados que expressam um fluxo de trabalho assíncrono adequado, sem operações de bloqueio. E você precisa escrever alguns testes e fazer com que tudo funcione (é uma linguagem de script e nomes de variáveis de dedilhado em gordura só são capturados no momento do teste de unidade). MAS, uma vez que você o faz funcionar, a área de superfície para os erros de heisenbugs - problemas estranhos que só se manifestam uma vez em um milhão de execuções - é uma área muito menor. Os impostos que escrevem o código Node.js. são fortemente carregados com antecedência na fase de codificação. Então você tende a acabar com um código estável.
- Pro: JavaScript é muito mais leve para expressar funcionalidade. É difícil provar isso com palavras, mas JSON , digitação dinâmica, notação lambda, herança prototípica, módulos leves, o que seja ... tudo isso tende a levar menos código para expressar as mesmas idéias.
- Con: Talvez você realmente goste de serviços de codificação em Java?
Para outra perspectiva sobre JavaScript e Node.js, confira De Java a Node.js , uma postagem no blog sobre as impressões e experiências de um desenvolvedor Java aprendendo o Node.js.
Módulos
Ao considerar nó, tenha em mente que a sua escolha de bibliotecas JavaScript vai DEFINE sua experiência. A maioria das pessoas usa pelo menos dois, um auxiliar de padrão assíncrono (Step, Futures, Async) e um módulo de açúcar JavaScript ( Underscore.js ).
Auxiliar / JavaScript Sugar:
- Underscore.js - use isso. Apenas faça. Isso torna seu código agradável e legível com coisas como _.isString () e _.isArray (). Não tenho muita certeza de como você poderia escrever um código seguro. Além disso, para obter uma linha de comando aprimorada fu, confira meu próprio Underscore-CLI .
Módulos de padrão assíncrono:
- Etapa - uma maneira muito elegante de expressar combinações de ações seriais e paralelas. Minha recomendação pessoal. Veja minha postagem sobre como é o código da etapa.
- Futuros - maneira muito mais flexível (isso é realmente uma coisa boa?) De expressar pedidos através de requisitos. Pode expressar coisas como "inicie a, b, c em paralelo. Quando A e B terminam, inicie AB. Quando A e C terminam, inicie AC". Essa flexibilidade requer mais cuidado para evitar erros no seu fluxo de trabalho (como nunca chamar o retorno de chamada ou chamá-lo várias vezes). Veja o post de Raynos sobre o uso de futuros (este é o post que me fez "obter" futuros).
- Async - biblioteca mais tradicional com um método para cada padrão. Comecei com isso antes da minha conversão religiosa para a etapa e a subsequente percepção de que todos os padrões no Async podiam ser expressos na Etapa com um único paradigma mais legível.
- TameJS - Escrito por OKCupid, é um pré-compilador que adiciona um novo idioma primitivo "aguardar" para escrever elegantemente fluxos de trabalho seriais e paralelos. O padrão parece incrível, mas requer pré-compilação. Eu ainda estou decidindo sobre isso.
- StreamlineJS - concorrente do TameJS. Estou inclinado para Tame, mas você pode se decidir.
Ou para ler tudo sobre as bibliotecas assíncronas, consulte esta entrevista em painel com os autores.
Estrutura da Web:
- Expresse o framework Great Ruby on Rails-esk para organizar sites. Ele usa o JADE como um mecanismo de modelagem XML / HTML, o que torna a construção do HTML muito menos dolorosa, quase elegante até.
- jQuery Embora tecnicamente não seja um módulo de nó, o jQuery está rapidamente se tornando um padrão de fato para a interface do usuário do lado do cliente. O jQuery fornece seletores do tipo CSS para 'consultar' os conjuntos de elementos DOM que podem ser operados (manipuladores, propriedades, estilos, etc.). Na mesma linha, a estrutura CSS do Bootstrap do Twitter , o Backbone.js para um padrão MVC e o Browserify.js para agrupar todos os seus arquivos JavaScript em um único arquivo. Todos esses módulos estão se tornando padrões de fato; portanto, você deve pelo menos conferi-los se ainda não os ouviu.
Teste:
- JSHint - deve usar; Eu não usei isso no começo, o que agora parece incompreensível. O JSLint adiciona de volta várias verificações básicas que você obtém com uma linguagem compilada como Java. Parênteses incompatíveis, variáveis não declaradas, tipos de várias formas e tamanhos. Você também pode ativar várias formas do que chamo de "modo anal", onde verifica o estilo de espaço em branco e outros enfeites, o que é bom se essa é sua xícara de chá - mas o valor real vem da obtenção de feedback instantâneo sobre o número exato da linha em que você esqueceu um fechamento ")" ... sem precisar executar seu código e acessar a linha incorreta. "JSHint" é uma variante mais configurável de Douglas Crockford 's JSLint .
- Mocha concorrente de votos que eu estou começando a preferir. Ambas as estruturas lidam bem com o básico, mas padrões complexos tendem a ser mais fáceis de expressar no Mocha.
- Votos Votos é realmente bastante elegante. E ele imprime um relatório adorável (--spec) mostrando quais casos de teste foram aprovados / reprovados. Passe 30 minutos aprendendo e poderá criar testes básicos para seus módulos com o mínimo de esforço.
- Zombie - Teste sem cabeça para HTML e JavaScript usando JSDom como um "navegador" virtual. Coisas muito poderosas. Combine-o com o Replay para obter testes determinísticos extremamente rápidos de código no navegador.
- Um comentário sobre como "pensar em" testes:
- O teste não é opcional. Com uma linguagem dinâmica como o JavaScript, há muito poucas verificações estáticas. Por exemplo, passar dois parâmetros para um método que espera 4 não será interrompido até que o código seja executado. Barra bastante baixa para criar bugs em JavaScript. Testes básicos são essenciais para preencher a lacuna de verificação com os idiomas compilados.
- Esqueça a validação, basta executar seu código. Para todo método, meu primeiro caso de validação é "nada quebra", e é o caso que é acionado com mais frequência. Provar que seu código é executado sem gerar captura de 80% dos bugs e fará muito para melhorar sua confiança no código que você voltará e adicionará os casos de validação diferenciados que ignorou.
- Comece pequeno e quebre a barreira inercial. Somos todos preguiçosos e pressionados pelo tempo, e é fácil ver os testes como "trabalho extra". Então comece pequeno. Escreva o caso de teste 0 - carregue seu módulo e relate o sucesso. Se você se esforçar para fazer exatamente isso, a barreira inercial ao teste será quebrada. São menos de 30 minutos para fazer isso pela primeira vez, incluindo a leitura da documentação. Agora escreva o caso de teste 1 - chame um de seus métodos e verifique "nada quebra", ou seja, se você não recebe um erro de volta. O caso de teste 1 deve levar menos de um minuto. Com a inércia perdida, torna-se fácil expandir gradualmente sua cobertura de teste.
- Agora evolua seus testes com seu código. Não se intimide com a aparência do teste "correto" de ponta a ponta com servidores simulados e tudo mais. O código começa simples e evolui para lidar com novos casos; testes devem também. À medida que você adiciona novos casos e nova complexidade ao seu código, adicione casos de teste para exercitar o novo código. Ao encontrar erros, adicione verificações e / ou novos casos para cobrir o código defeituoso. Quando você estiver depurando e perder a confiança em um pedaço de código, volte e adicione testes para provar que está fazendo o que pensa. Capture seqüências de dados de exemplo (de outros serviços que você chama, sites que você raspa, o que for) e alimente-os com seu código de análise. Alguns casos aqui, validação aprimorada lá, e você terminará com um código altamente confiável.
Além disso, confira a lista oficial dos módulos Node.js. recomendados. No entanto, o Wiki de módulos de nós do GitHub é muito mais completo e um bom recurso.
Para entender o Node, é útil considerar algumas das principais opções de design:
O Node.js é BASEADO EM EVENTOS e ASSÍNCRONO / NÃO BLOQUEADO. Eventos, como uma conexão HTTP recebida, disparam uma função JavaScript que faz um pouco de trabalho e iniciam outras tarefas assíncronas, como conectar-se a um banco de dados ou extrair conteúdo de outro servidor. Depois que essas tarefas são iniciadas, a função de evento termina e o Node.js volta a dormir. Assim que outra coisa acontece, como a conexão do banco de dados sendo estabelecida ou o servidor externo respondendo ao conteúdo, as funções de retorno de chamada são acionadas e mais código JavaScript é executado, potencialmente iniciando ainda mais tarefas assíncronas (como uma consulta ao banco de dados). Dessa forma, o Node.js entrelaçará alegremente atividades para vários fluxos de trabalho paralelos, executando quaisquer atividades que sejam desbloqueadas a qualquer momento. É por isso que o Node.js faz um ótimo trabalho gerenciando milhares de conexões simultâneas.
Por que não usar apenas um processo / thread por conexão como todos os outros?No Node.js, uma nova conexão é apenas uma alocação de heap muito pequena. A ativação de um novo processo requer significativamente mais memória, um megabyte em algumas plataformas. Mas o custo real é a sobrecarga associada à alternância de contexto. Quando você tem 10 ^ 6 threads de kernel, o kernel precisa trabalhar bastante para descobrir quem deve executar a seguir. Um monte de trabalho foi dedicado à construção de um planejador O (1) para Linux, mas no final, é muito mais eficiente ter um único processo orientado a eventos do que 10 ^ 6 processos competindo pelo tempo da CPU. Além disso, sob condições de sobrecarga, o modelo de multiprocessos se comporta muito mal, passando por serviços críticos de administração e gerenciamento, especialmente SSHD (o que significa que você não pode nem entrar na caixa para descobrir como está realmente ferrado).
O Node.js é SINGLE THREADED e LOCK FREE . O Node.js, como uma opção de design muito deliberada, possui apenas um único encadeamento por processo. Por esse motivo, é fundamentalmente impossível que vários threads acessem dados simultaneamente. Portanto, nenhum bloqueio é necessário. Threads são difíceis. Realmente muito difícil. Se você não acredita nisso, não fez programação em threads suficiente. Acertar o bloqueio é difícil e resulta em erros realmente difíceis de rastrear. Eliminar bloqueios e multithreading faz com que uma das classes mais desagradáveis de bugs desapareça. Essa pode ser a maior vantagem do nó.
Mas como aproveito minha caixa de 16 núcleos?
Dois caminhos:
- Para grandes tarefas de computação pesada, como codificação de imagens, o Node.js pode iniciar processos filho ou enviar mensagens para processos de trabalho adicionais. Nesse design, você teria um thread gerenciando o fluxo de eventos e N processos executando tarefas pesadas de computação e mastigando as outras 15 CPUs.
- Para dimensionar a taxa de transferência em um serviço da Web, você deve executar vários servidores Node.js. em uma caixa, uma por núcleo, usando cluster (com Node.js. v0.6.x, o módulo "cluster" oficial vinculado aqui substitui a versão learnboost que possui uma API diferente). Esses servidores locais do Node.js. podem competir em um soquete para aceitar novas conexões, equilibrando a carga entre eles. Depois que uma conexão é aceita, ela se torna fortemente vinculada a um único desses processos compartilhados. Em teoria, isso parece ruim, mas, na prática, funciona muito bem e permite evitar a dor de cabeça ao escrever códigos seguros para threads. Além disso, isso significa que o Node.js obtém excelente afinidade do cache da CPU, mais efetivamente usando a largura de banda da memória.
O Node.js permite que você faça coisas realmente poderosas sem suar a camisa. Suponha que você tenha um programa Node.js. que executa uma variedade de tarefas, ouve em umaporta TCP comandos, codifica algumas imagens, qualquer que seja. Com cinco linhas de código, você pode adicionar um portal de gerenciamento da Web baseado em HTTP que mostra o status atual das tarefas ativas. Isso é FÁCIL de fazer:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(myJavascriptObject.getSomeStatusInfo());
}).listen(1337, "127.0.0.1");
Agora você pode acessar um URL e verificar o status do seu processo em execução. Adicione alguns botões e você terá um "portal de gerenciamento". Se você possui um script Perl / Python / Ruby em execução, apenas "inserir um portal de gerenciamento" não é exatamente simples.
Mas o JavaScript não é lento / ruim / mal / cria-do-diabo? O JavaScript tem algumas esquisitices estranhas, mas com "as partes boas" existe uma linguagem muito poderosa e, de qualquer forma, o JavaScript é a linguagem do cliente (navegador). O JavaScript está aqui para ficar; outros idiomas o visam como uma IL, e talentos de classe mundial estão competindo para produzir os mais avançados mecanismos JavaScript. Devido ao papel do JavaScript no navegador, uma enorme quantidade de esforço de engenharia está sendo lançada para tornar o JavaScript extremamente rápido. V8é o melhor e mais recente mecanismo javascript, pelo menos para este mês. Ele impressiona as outras linguagens de script em eficiência e estabilidade (olhando para você, Ruby). E isso só vai melhorar com grandes equipes trabalhando no problema da Microsoft, Google e Mozilla, competindo para criar o melhor mecanismo JavaScript (não é mais um "intérprete" JavaScript, pois todos os mecanismos modernos fazem toneladas de JITcompilando sob o capô com interpretação apenas como um substituto para o código de execução única). Sim, todos nós desejamos poder corrigir algumas das opções mais estranhas da linguagem JavaScript, mas não é tão ruim assim. E a linguagem é tão flexível que você realmente não está codificando JavaScript, está codificando Step ou jQuery - mais do que qualquer outra linguagem, em JavaScript, as bibliotecas definem a experiência. Para criar aplicativos da Web, você precisa conhecer o JavaScript de qualquer maneira, portanto, a codificação no servidor tem uma espécie de sinergia de conjunto de habilidades. Isso me fez não temer escrever código de cliente.
Além disso, se você realmente odeia JavaScript, pode usar açúcar sintático como o CoffeeScript . Ou qualquer outra coisa que crie código JavaScript, como o Google Web Toolkit (GWT).
Falando em JavaScript, o que é um "fechamento"? - Uma maneira bastante elegante de dizer que você retém variáveis de escopo lexicamente nas cadeias de chamadas. ;) Como isso:
var myData = "foo";
database.connect( 'user:pass', function myCallback( result ) {
database.query("SELECT * from Foo where id = " + myData);
} );
// Note that doSomethingElse() executes _BEFORE_ "database.query" which is inside a callback
doSomethingElse();
Veja como você pode simplesmente usar "myData" sem fazer nada de estranho, como esconder em um objeto? E, diferentemente do Java, a variável "myData" não precisa ser somente leitura. Esse poderoso recurso de linguagem torna a programação assíncrona muito menos detalhada e menos dolorosa.
Escrever código assíncrono sempre será mais complexo do que escrever um script simples de thread único, mas com o Node.js, não é muito mais difícil e você obtém muitos benefícios, além da eficiência e escalabilidade de milhares de conexões simultâneas. ..