Skip to content

Instantly share code, notes, and snippets.

@igorlima
Last active August 29, 2015 14:10
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save igorlima/8908b0ee4aa0eab17ab7 to your computer and use it in GitHub Desktop.

Um recurso que é bastante solicitado é a capacidade de incorporar o Node em outras aplicações, em especial de uma maneira que permita a integração com outros loops de evento e com suporte para múltiplos contextos de execução: ou seja, a capacidade de ter várias instâncias Node coexistindo pacificamente dentro do mesmo processo. Imagine uma aplicação node-webkit, onde cada janela executa sua própria instância de Node e que fique isolada de todas as outras janelas. Ou imagine o Node sendo incorporado em um telefone ou num switch de rede onde há a execução de lógica de roteamento para múltiplas conexões, mas em um único processo (o que não está muito longe).

GitHub veio até nós precisando desse tipo de funcionalidade para o Atom.io. É pedir demais, justamente porque o Node começou como – e ainda é – uma aplicação single-threaded (de uma única thread), construído em torno do conceito de um único loop de evento, e com centenas de variáveis globais que armazenam vários bits de estado.

Você pode imaginar como que o reajuste de thread-safe em cada base de código é uma tarefa altamente sujeita a erros e é por isso que nós não fizemos – ou melhor, ainda não está feito. Nós queríamos fazer essa mudança de uma forma acessível ao cliente e em passo incremental que iria ajudar toda a comunidade. Aliás, se você precisar de algumas mudanças no Node, mas está sem tempo para descobrir como fazer alterações, nós podemos lhe ajudar!

Introdução a contexto múltiplo

Em vez disso, fizemos com que isso seja possível no Node v0.12 para possibilitar o uso de múltiplos contextos de execução de dentro do mesmo loop de eventos. Mas não se preocupe: do ponto de vista do usuário, não há nenhuma mudança visível, tudo continua funcionando como era antes. Mas caso você seja um embedder ou um autor de complemento nativo add-on, continue a leitura!

O trabalho de “contexto múltiplo” que está no commit 756b622 é primeiramente uma limpeza de job: isso ocorre em todos os globals, eliminando aqueles que não precisam ser global de imediato e transformando os restantes em propriedades de contexto de execução. Isso não nos dá a segurança de thread ainda, mas já é um primeiro e importante passo.

A segunda parte está fazendo os internals do Node ter conhecimento da existência de múltiplos contextos de execução. Em toda parte, as runtime do C++ chama a máquina virtual V8, e é preciso ter certeza de que o primeiro contexto foi entrado corretamente. Um rápido exemplo:

function onconnect() {
this.write('GET / HTTP/1.1\r\n' +
'Host: strongloop.com\r\n' +
'\r\n');
this.pipe(process.stdout);
}
require('net').connect(80, 'strongloop.com', onconnect);

Execução-sábia, é algo do tipo:

<enter VM>
connect(80, 'strongloop.com'); // kernel reports EINPROGRESS here
<leave VM>
<wait for TCP handshake to complete>
<enter VM>
onconnect();
<leave vm>

Entre as chamadas na VM, é possível mudar o contexto de execução. Portanto é essencial que connect() lembre do contexto atual e que a chamada onconnect() restaure esse contexto. Se não fizer isso pode provocar alguns bugs que são realmente difíceis de rastrear.

Felizmente, o Node cuida disso automaticamente; embedders e autores de complemento nativo não precisam se preocupar com isso, a menos que fazem chamadas para a VM com v8::Function::Call() em vez de chamar node::MakeCallback().

Se você é um autor de complemento add-on e quer fazer o seu complemento sensível a contexto, veja abaixo o que é preciso fazer:

* Instead of NODE_MODULE(), use NODE_MODULE_CONTEXT_AWARE(). Before:
 
void Initialize(v8::Handle<v8::Object> exports) {
// ...
}
NODE_MODULE(foo, Initialize)

Depois:

void Initialize(v8::Handle<v8::Object> exports,
v8::Handle<v8::Value> module,
v8::Handle<v8::Context> context) {
// ...
}
NODE_MODULE_CONTEXT_AWARE(foo, Initialize);

Seu inicializador sensível a contexto é chamado uma vez a cada contexto criado por embedder. Ainda não há nenhuma função de limpeza por contexto. Eventualmente, node::AtExit() irá preencher esse papel, mas por agora ainda é um evento por processo. Caso isso seja um problema para você, por favor, registre um bug aqui.

Para transformar variáveis globais em propriedades de contexto, existem várias maneiras de fazer isso, mas uma opção é reivindicar um índice de dados embedder manualmente (por conta própria) e armazená-la tudo lá.

Ainda não há nenhum registro global para os índices, portanto é possível que haja conflitos. Alguém quer tentar dar uma mão em um patch? Nesse meio tempo, escolha um número aleatório large-ish (diga-se entre 2^10 e 2^16) mas não exagere: a máquina virtual V8 usa um array não muito esparso como armazenamento de backup para índices embedder. Alegando que o índice 1 << 29 irá consumir muita memória!

Quanto que falta pra chegar lá?

Ainda existe algumas edges que precisam ser polidas: process.chdir() muda o diretório de trabalho de todos os contextos, ao invés de apenas mudar o contexto da chamada; os add-ons de carregamento de múltiplos contextos ainda tem alguns casos de edge e assim por diante. Mas isso é um grande cuidado para mantermos uma aparência elegante e uma nítida eficiência. A estrutura básica já está no lugar e está sendo usada com sucesso pela empresa que patrocinou esse esforço para o contexto múltiplo. Além do mais, isso abre um caminho para um verdadeiro multi-threaded com locação múltipla. É improvável que isso aconteça antes da versão v0.12 do Node, mas talvez veremos na versão v1.0 do Node – É só nós ousarmos!

Use StrongOps para monitorar aplicações Node

Pronto para começar a monitorar loops de evento, gerenciar clusters e perseguir os vazamentos de memória? Nós fizemos isso de tal forma que fique bem mais fácil com o StrongOps tanto localmente quanto na sua nuvem favorita, simplesmente com um npm install.

Use StrongOps para monitorar aplicações Node

E o que mais?

Nota do editor: Robert Wang escreveu um esclarecimento sobre como o nó-webkit funciona em relação ao exemplo que foi citado. “Diferentes janelas do nó-webkit compartilham uma mesma instância do Node, enquanto estão no mesmo processo de renderização, que é o caso padrão. O programador pode optar por abrir uma janela em um novo processo de renderização, passando a opção new-instance, em qualquer caso uma nova instância de Node é iniciada.”

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment