Comunique-se para trás em um tubo

4

Eu tenho um pipeline simples:

node foo.js | node bar.js

bar.js lerá de stdin para obter dados de foo.js .

Mas o que eu quero fazer é garantir que bar.js receba uma das últimas mensagens de foo.js antes que foo.js decida não sair. Essencialmente, quero criar um padrão simples de solicitação / resposta.

foo writes to stdout --> bar reads from stdin --> how can bar send a message back to foo?

Existe uma maneira de se comunicar para trás em um pipeline ou nunca deveria haver uma necessidade de fazer isso?

    
por Alexander Mills 13.11.2017 / 18:14

3 respostas

3

Não. Um pipeline é um canal de comunicação unidirecional. É por isso que é chamado de "pipeline"; você também não poderia enviar o óleo de volta a um pipeline se você tentasse.

No entanto, se o bar.js tiver que falar com o foo.js também, você tem algumas opções:

  • Crie um soquete de domínio unix em vez de um pipeline e inicie os dois foo.js e bar.js separadamente (ou seja, não direcione mais a saída de foo.js para bar.js). Eu não sei como você faz isso a partir do nó, mas essencialmente um soquete de domínio unix é um soquete de rede que usa nomes de arquivos em vez de endereços IP e funciona dentro do kernel. Os soquetes são destinados à comunicação bidirecional, mas requerem mais configurações do que um simples pipe (por exemplo, um soquete de escuta pode falar com várias instâncias de bar.js). Você pode encontrar soquetes de domínio unix no sistema de arquivos, mas não é estritamente necessário (e, de fato, o Linux permite criar um soquete de domínio unix sem um rastreio no sistema de arquivos).
  • Use mkfifo para criar um pipe nomeado (ou use alguma API do nó para criar um, se ele existir; novamente, eu não sei o nó). Em seguida, em foo.js , abra esse pipe nomeado e leia-o. Seu script bar.js pode abrir o mesmo pipe nomeado e gravar nele.

O último será o mais fácil para a transição, pois você ainda usa E / S de arquivo (abrir um canal nomeado requer a abertura de um arquivo no sistema de arquivos), mas ainda seria unidirecional (embora você tivesse dois canais, um em cada direção). O primeiro é um pouco mais limpo e também permite migrar com mais facilidade um dos dois scripts para um host diferente, se isso for necessário.

De qualquer forma, se os seus scripts agora estão se comunicando bidirecionalmente, então, para maior clareza, sugiro que você os inicie como processos separados, em vez de ter um canal de processo no outro. IMHO, eles são parceiros iguais agora, e sua linha de comando deve mostrar isso. Isso é apenas um detalhe, e certamente não é tecnicamente necessário.

    
por 13.11.2017 / 18:27
3

Em sistemas onde os pipes são bidirecionais (NetBSD, FreeBSD, Unices derivados do SVR4 (todos aqueles onde pipes usam STREAMS no mínimo), mas não Linux):

node foo.js <&1 | node bar.js >&0

Além do pipe nomeado já mencionado, você também pode usar um socketpair:

perl -MSocket -e '
  socketpair(A, B, AF_UNIX, SOCK_STREAM, PF_UNSPEC);
  if (fork) {
    open STDIN, "<&A";
    open STDOUT, ">&B";
    exec "node", "foo.js";
  } else {
    open STDIN, "<&B";
    open STDOUT, ">&A";
    exec "node", "bar.js";
  }'

Ou dois canais sem nome, por exemplo, usando um coproc .

com zsh :

coproc node foo.js
node bar.js <&p >&p

ksh :

node foo.js |&
node bar.js <&p >&p

bash 4 +:

coproc node foo.js
node bar.js <&"${COPROC[0]}" >&"${COPROC[1]}"

Ou com o operador% pipe yash :

{ node foo.js <&3 3<&- | node bar.js 3<&-; } >>|3
    
por 30.11.2017 / 00:43
0

É muito fácil se comunicar de trás para frente, embora provavelmente não seja recomendado. É claro que você pode obter alguns ciclos de feedback realmente agradáveis / desagradáveis em um sistema complexo, comunicando o caminho errado.

No * nix você poderia fazer isso com o Node.js da seguinte forma:

// foo.js
process.stdout.write(process.pid);

// bar.js
process.stdin.resume().once('data', function(pid){

   const writable = fs.createWriteStream('/proc/${pid}/fd/0');
   writable.write('whatevs');  // write to stdin of foo.js

});

o acima é simplificado, mas você tem a ideia. O ideal seria usar o JSON para codificar as mensagens do stdio. Aqui está uma boa biblioteca para fazer isso: link

No entanto, o problema é que, no MacOS, você não pode usar /proc/<pid> ... Da última vez que verifiquei, a única boa maneira de fazer isso seria usar mkfifo para criar uma% exclusivafifo para esta situação.

    
por 13.11.2017 / 19:35