Tenho 2 aplicativos que canalizam seus dados:
aplicação1 | aplicação2
Basicamente, application1 gera um log com eventos que application2 processa. O problema é que eu frequentemente atualizo application2. Ou seja, eu preciso parar isso, atualizar os binários e reiniciá-lo. Nessa pequena duração application1 pode perder dados.
Li sobre usar pipes nomeados usando mkfifo
e pensei que poderia ser uma solução ideal. Mantenha o application1 em execução e faça com que ele grave no pipe nomeado com suporte de arquivo para que, quando o aplicativo for atualizado, nenhum dado seja perdido e, quando o application2 iniciar, ele obtenha os dados.
Testar isso com cat
para emular leitor/escritor funciona até que não haja mais um leitor. Isso é inesperado.
Uma alternativa seria usar um arquivo real, mas isso tem problemas:
- Ele permanece no disco e não age como FIFO
- É necessário algum tipo de rotação para evitar que os arquivos fiquem muito grandes
- AFAIK quando o leitor estiver no final (cauda?), será necessário sondar atrás de um cronômetro se o arquivo tiver aumentado de tamanho, o que aumentou a latência do processamento
Estou no controle do leitor, o comportamento atual já é que ele irá reiniciar automaticamente, mas não estou no controle do escritor. Só posso canalizar sua saída para outra coisa.
- Os pipes nomeados podem ser configurados de forma que sejam duráveis?
- Li sobre "fixar" no tubo pelo escritor, mas não consigo fazer isso funcionar
- Posso evitar que um pipe seja fechado quando o leitor sair?
- Existem alternativas que se comportam como um cano?
A solução https://unix.stackexchange.com/a/784153/585995 funciona!
mkfifo /tmp/mypipe
writerapp 1<>/tmp/mypipe
O escritor continua em execução enquanto eu reiniciaria o leitor:
readerapp </tmp/mypipe
a propósito, você pode testar isso sozinho usandocat
Você pode considerar os pipes nomeados como uma espécie de âncoras para a criação de pipes regulares.
Sobre:
O shell faz um
open("fifo", O_WRONLY)
. Queopen()
bloqueia até que outro processo (ou o mesmo processo) faça umopen("fifo", O_RDONLY)
¹:Nesse ponto, um pipe (muito semelhante aos sem nome criados por
pipe()
) é instanciado.Se
application2
terminar, a extremidade de leitura desse pipe será fechada, tornando-se um pipe quebrado eapplication1
recebendo um SIGPIPE na próxima vez que tentar gravar nele.Para evitar isso, você deve garantir que o pipe seja mantido ativo, por exemplo, tendo outro processo que tenha um fd aberto para leitura no pipe, ou você pode
application1
abrir o modofifo
in¹O_RDWR
para que ele se torne um escritor e leitor daquele pipe, mesmo que não vá realmente ler a partir dele.Com:
O Stdout é aberto em O_RDWR e o pipe é imediatamente instanciado.
application1
começará a escrever no pipe até que o buffer do pipe esteja cheio. Se quisesse,application1
poderia ler do seu stdout fd e consumir o que escreveu antes, mas os aplicativos normalmente não leem do seu stdout.Então, você faz:
application2
se tornará outro leitor daquele pipe. Se ele terminar, o pipe não será quebrado porque ainda há um leitor para ele (application1
), e você pode iniciar a nova versão deapplication2
as:application2 < fifo
que continuará de onde a execução anterior parou.Com pipes sem nome, você também pode fazer algo como:
Onde você fornecerá novas versões do seu
application2
asnew-application2
², que será renomeado automaticamente e executado em loop.O loop para quando
application2
oumv
falha, momento em que o pipe será quebrado eapplication1
receberá um SIGPIPE na próxima vez que tentar gravar nele.¹ Se bem me lembro, isso não funciona com todos os Unices, mas não sei dizer em quais não funciona.
² Você vai querer ter certeza
new-application2
de que ele aparece como um todo (por exemplo, renomeando-o de um nome/local anterior) para evitar o risco de ele ser executado sem estar totalmente criado.Dependendo da sua definição de durável, sim.
mkfifo
Cria o fifo ou pipe nomeado, e ele permanece lá até você excluí-lo.Não tenho certeza do que você quer dizer com isso. No entanto, se você não estiver no controle do escritor, isso não seria uma solução.
Você pode usar
tail -f
no fifo, em vez de umcat
. Ou mesmotail -n +1 -f fifo_name | command
Algumas informações adicionais: quando
cat
encontra o fim do arquivo, ele sai. Então, se você fizer issoe em outro terminal
você obtém o salto do
cat
, e o gato sai. Se o seu escritor então fizerele fica travado até que um novo
cat fifootje
seja emitido.tail -f
("follow" ou "forever") apenas espera até que algumas linhas sejam adicionadas e não sai até ser morto. Então,e no terminal do escritor:
Alternativamente, você pode:
Sim, soquetes.