Request: Muitos sockets do Ubuntu com 'CLOSE_WAIT'

Criado em 28 out. 2016  ·  33Comentários  ·  Fonte: request/request

versão do pedido: 2.76.x
sistema: Ubuntu

Estou usando solicitação via proxy com TLS e, depois de muito tempo, tenho milhares de portas de soquete no status CLOSE_WAIT , procuro no Google e descubro que talvez a força do servidor remoto feche meu soquete levaria a isso, mas o que eu quero é detectar o evento e fechar o soquete normalmente ou as portas se esgotarão e o processo nunca funcionará.

Dose isso relacionado a # 2438?

Obrigado.

stale

Comentários muito úteis

Ei, @mikeal , você tem chance de mesclar meu pedido com o agente de túnel? Tem grande impacto para programas de longa duração com proxy. Atualmente, temos que escrever npm-shrinkwrap para reverter a versão. Você viu que muitas pessoas têm o mesmo problema e meu código o corrige bem, mencionado neste problema. Quero saber como você pode mesclá-lo e, se não é o responsável por esse repo, com quem mais devo entrar em contato? Ou há alguma outra etapa que eu perdi. Também pls preste atenção a este comentário @simov @mscdex , Espero fazê-lo recentemente, muito obrigado. :)

Todos 33 comentários

Alguém pode ajudar? Porque o servidor proxy fecharia meu soquete quando eu solicitasse muito rápido, então é necessário fechar a conexão do soquete normalmente para evitar que o número de portas aumente continuamente. Agora eu tenho milhares de portas com status CLOSE_WAIT.

Encontro o código de request.js abaixo e meu código de erro é ECONNRESET. Não tenho certeza se o código abaixo fecharia o soquete de solicitação?

Request.prototype.onRequestError = function (error) {
  var self = this
  if (self._aborted) {
    return
  }
  if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET'
      && self.agent.addRequestNoreuse) {
    self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
    self.start()
    self.req.end()
    return
  }
  if (self.timeout && self.timeoutTimer) {
    clearTimeout(self.timeoutTimer)
    self.timeoutTimer = null
  }
  self.emit('error', error)
}

/ cc @mscdex

Não parece que isso esteja relacionado a # 2438.

@ mike442144 O downgrade para request 2.75.0 corrige o problema?

@mscdex Fiz o downgrade para 2.69.0, também há milhares de sockets CLOSE_WAIT.
Eu li o código tunnel-agent e o código request , abaixo está o segmento de código relevante:

tunnel-agent / index.js

  function onConnect(res, socket, head) {
    connectReq.removeAllListeners()
    socket.removeAllListeners()

    if (res.statusCode === 200) {
      assert.equal(head.length, 0)
      debug('tunneling connection has established')
      self.sockets[self.sockets.indexOf(placeholder)] = socket
      cb(socket)
    } else {
      debug('tunneling socket could not be established, statusCode=%d', res.statusCode)
      var error = new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode)
      error.code = 'ECONNRESET'
      options.request.emit('error', error)
      self.removeSocket(placeholder)
    }
  }

  function onError(cause) {
    connectReq.removeAllListeners()

    debug('tunneling socket could not be established, cause=%s\n', cause.message, cause.stack)
    var error = new Error('tunneling socket could not be established, ' + 'cause=' + cause.message)
    error.code = 'ECONNRESET'
    options.request.emit('error', error)
    self.removeSocket(placeholder)
  }

request.js

self.req.on('error', self.onRequestError.bind(self))


Request.prototype.onRequestError = function (error) {
  var self = this
  if (self._aborted) {
    return
  }
  if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET'
      && self.agent.addRequestNoreuse) {
    self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
    self.start()
    self.req.end()
    return
  }
  if (self.timeout && self.timeoutTimer) {
    clearTimeout(self.timeoutTimer)
    self.timeoutTimer = null
  }
  self.emit('error', error)
}

As únicas questões com as quais devemos nos preocupar são:
1. Para o erro socket hang up , o que o soquete subjacente fará? se o soquete não fechar porque há dados restantes para enviar, como forçá-lo a fechá-lo?
2. Para que statusCode não seja 200 , o que o soquete subjacente fará? fechar como de costume?
Os dois erros são chamados de ECONNRESET no módulo de solicitação, mas o primeiro é realmente um erro de soquete, o segundo é uma espécie de erro de http. Portanto, podemos resolver esse problema quando respondermos às duas perguntas.

Muito obrigado. É muito gentil da sua parte ajudar.

ECONNRESET nunca é uma coisa http, significa que a outra extremidade fechou abruptamente / forçosamente a conexão TCP por qualquer motivo. Eu não sei por que erros artificiais ECONNRESET estão sendo criados, porém, isso parece errado para mim.

Como não estou familiarizado com a parte de proxy da base de código, não sei o que sugerir, exceto usar um http / https.Agent que tem um limite para o número máximo de sockets (as instâncias de Agente globais padrão não têm limites em versões modernas de nó) e / ou usar keep-alive no referido Agente tanto quanto possível?

@mscdex de acordo com o código em tunnel-agent/index.js :

var connectReq = self.request(connectOptions) // this is same as http.request
connectReq.once('connect', onConnect)   // for v0.7 or later

onConnect é um callback para connect evento de req , e se statusCode não for igual a 200, emite um erro em req , nesta situação um socket a comunicação foi concluída e o cliente recebeu um pacote http completo, portanto, é um problema de http. Por exemplo, se solicitamos um site, mas obtivemos uma resposta com statusCode 500, é totalmente a mesma coisa. A diferença é que tunnel-agent/index.js emite manualmente um erro com o código ECONNRESET em req , mas request.js não consegue descobrir qual é um problema de soquete e qual não é.

Muito obrigado.

Alguém pode ajudar?

Eu tive uma questão semelhante. Eu tenho este servidor X (Tomcat / Java) que tinha algumas APIs legadas implantadas nele. Eu precisava ignorar todas aquelas APIs por meio do meu servidor de nó. Para esse propósito, estou usando pedido-promessa-nativo que se baseia em 'pedido'.

Do nada, o servidor X começou a demorar muito para responder. Eu pensei que era o pacote NPM que estava bagunçando tudo. Mas depois percebi que o servidor X estava levando para algum outro (db) e não fechava a conexão (sockets) corretamente. Por causa disso, ele tinha centenas de portas ativas. Eu reiniciei esse servidor e ele começou a funcionar bem.

Consegui descobrir executando 'netstat' no servidor e verificando o endereço estrangeiro.

TL; DR Era o servidor X que estava bagunçando as coisas, não este pacote em si.

Espero que ajude.

@harshitgupta Muito obrigado, estou fazendo isso para confirmar a solicitação de tratamento do evento de fechamento de soquete corretamente, porque utilizo o módulo de solicitação como um cliente para me conectar a um servidor proxy e recebo muitas mensagens ECONNRESET, preciso saber se este módulo fecha o soquete ou não. Se não, por que e como?
Veja o diagrama:

_Server está pronto para fechar o socket_
Servidor ---> FIN ---> Cliente
Servidor <--- ACK <--- Cliente
_Server está em FIN_WAIT_2, e o cliente está em CLOSE_WAIT_
Servidor <--- FIN <--- Cliente
_Após o cliente enviar FIN para o servidor, o cliente será LAST_ACK_
Servidor ---> ACK ---> Cliente

Como acima, nosso programa não envia pacotes FIN para o servidor.

Eu capturei os pacotes entre o servidor proxy e meu processo e renomeei o IP src como local ,

local -> sdr [SYN]
sdr -> local [SYN, ACK]
local -> sdr [ACK]
sdr -> local [ACK]
local -> sdr [ACK]
local -> sdr [ACK]
_abaixo a mensagem é que o servidor enviou o pacote FIN para fechar o socket_
sdr -> local [FIN, ACK]
local -> sdr [ACK]

Obviamente, meu processo não enviou FIN para notificar o servidor. Alguém que sabe como fechar manualmente o soquete subjacente?

Atualizado: a solução abaixo não funciona !!!! Parece que não consigo obter o evento 'final' no soquete subjacente se a conexão for estabelecida e o statusCode não for 200, mas o evento 'final' recebido quando onError. Porque? será um bug de net ? parece ter algo a ver com soquete !!

Posso corrigir o problema agora, mas gostaria de confirmar algum código em tunnel-agent , Quem é o autor de tunnel-agent ? parece que ninguém está familiarizado com este repo? @simov @mscdex @mikeal

Eu testei o agente de túnel por alguns dias e finalmente descobri que o número de CLOSE_WAIT era igual ao número de erro cujo statusCode não era igual a 200 na função onConnect , aqui está o código e a solução:

///// in createSocket function:

var connectReq = self.request(connectOptions)
  connectReq.useChunkedEncodingByDefault = false // for v0.6
  connectReq.once('response', onResponse) // for v0.6
  connectReq.once('upgrade', onUpgrade)   // for v0.6
  connectReq.once('connect', onConnect)   // for v0.7 or later
  connectReq.once('error', onError)
  connectReq.once('socket', function(socket){
    /* The solution, just send FIN packet to server if received FIN packet 
    * despite the pending data queue.
    */
        socket.on('end',function(){
            socket.end();
        });
    });
  connectReq.end()

function onConnect(res, socket, head) {
    connectReq.removeAllListeners()
    socket.removeAllListeners()

   if (res.statusCode === 200) {
      assert.equal(head.length, 0)
      debug('tunneling connection has established')
      self.sockets[self.sockets.indexOf(placeholder)] = socket
      cb(socket)
    } else {

    /*in this condition path, the socket will enter into CLOSE_WAIT status,
    * because the server sent FIN packet to us,but we have pending data in queue
    * so that we do not send FIN packet. 
    */ 
      debug('tunneling socket could not be established, statusCode=%d', res.statusCode)
      var error = new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode)
      error.code = 'ECONNRESET'
      options.request.emit('error', error)
      self.removeSocket(placeholder)
    }
  }

Documento de soquete sobre end : net_event_end

Mas, não tenho certeza por que o soquete deve remover todos os ouvintes? socket.removeAllListeners() , há alguma armadilha?

@simov @mscdex @mikeal
Eu encontrei o problema, consulte o código de duas linhas na função onConnect do agente de túnel:

 function onConnect(res, socket, head) {
    connectReq.removeAllListeners()
    socket.removeAllListeners()
  }

Por que ???? Por que ele remove todos os ouvintes? Obviamente, não poderei receber o evento end depois disso! Neste momento, um pacote FIN está chegando, então nenhum órgão será responsável pelo problema de fechamento do soquete!

@mscdex @simov Por favor mescle a solicitação de pull, eu

Não estou tão familiarizado com o agente de túnel. O autor original desse código o mantém aqui: https://github.com/koichik/node-tunnel

Eu verifiquei as duas bases de código e localizei apenas pequenas diferenças visuais e, de fato, nosso conjunto de testes aqui solicitado está sendo aprovado em qualquer um dos módulos. Portanto, prefiro mudar para o módulo tunnel e, se você ainda tiver o mesmo problema, abra um PR lá.

Você pode modificar lib/tunnel.js e testar com ele:

-, tunnel = require('tunnel-agent')
+, tunnel = require('tunnel')

Bom trabalho neste assunto: +1:

@simov Já li o código fonte do túnel, ainda tem esse problema, vou abrir um PR. e eu sugiro que você mude para o túnel original após a fusão do PR. Eu vou te dizer.

@simov O autor original @koichik parece não manter mais o

@ mike442144 oi, tenho o mesmo ambiente com você. você pode mostrar todo o código? obrigada.

quero dizer, quero ver como usar o retorno de chamada de erro ao atualizar para a sua versão de código

@kazaff , consulte https://github.com/bda-research/tunnel-agent/tree/callback , é fácil de consertar, mas profundo o suficiente. Antes de mesclar para master e republicar, você pode usar meu repo como uma dependência.

@ mike442144 muito obrigado ~

e, gostaria de saber se preciso alterar meu código de negócios para adaptar seu repo.

eu li o código-fonte por conta própria. e com base na sua solução, confundo no evento de erro de solicitação. agora eu adiciono algum código, é parecido com:

var socketHold = null;    // add new code
Request
.get({/*some config*/}, function(err, response){/*my original code*/})
.on('error', function(error){
  // add new code
  console.error('Request on error');
  console.error(error.stack);
  if(socketHold){
    socketHold.emit('free');
  }
}).on('socket', function(socket){
   socketHold = socket;
});

eu preciso fazer assim? ou simplesmente não fazer nada, apenas alterar a dependência do seu repo?

preciso de sua ajuda, muito obrigado.

@kazaff O que você deve fazer é usar npm-shrinkwrap.json para substituir a versão do agente de túnel. Não precisa fazer nada com o seu código.

{
    "name": "your project name",
    "version": "your project version",
    "dependencies": {
    "tunnel-agent": {
        "version": "0.4.3",
        "from": "tunnel-agent@>=0.4.1 <0.5.0",
        "resolved": "git://github.com/bda-research/tunnel-agent.git#callback"
    }
    }
}

É isso.

@ mike442144 ok, legal. vou tentar.

Percebo que @mikeal tem uma nova versão publicada do agente de túnel, que interrompe meu projeto. Porque a versão apenas entre 0.4.1 a 0.5.0 usará meu próprio repo.
Então, @mikeal , você vai mesclar minha solicitação de pull, que testei por cerca de 4 meses. Ou alguma sugestão?
Para uma solução temporária, vamos modificar o campo from para> = 0.4.1 apenas.

link para PR?

@mikeal Como você vai lidar com o problema? É só perguntar, já que não vi nenhuma atualização :)

1 Estou executando no Ubuntu e esse problema pode facilmente fazer com que o nó se transforme em milhares de soquetes. Além disso, havia um PR que aplicava options.timeout à solicitação CONNECT, que também seria útil.

Alguém está olhando para isso? Eu também sou afetado por isso

@mattdonnelly Use meu repo como sua dependência, git: //github.com/bda-research/tunnel-agent.git#timeout e adicione npm-shrinkwrap.json como mencionei acima.

Este problema foi automaticamente marcado como obsoleto porque não teve atividades recentes. Ele será fechado se nenhuma outra atividade ocorrer. Obrigado por suas contribuições.

Ei, @mikeal , você tem chance de mesclar meu pedido com o agente de túnel? Tem grande impacto para programas de longa duração com proxy. Atualmente, temos que escrever npm-shrinkwrap para reverter a versão. Você viu que muitas pessoas têm o mesmo problema e meu código o corrige bem, mencionado neste problema. Quero saber como você pode mesclá-lo e, se não é o responsável por esse repo, com quem mais devo entrar em contato? Ou há alguma outra etapa que eu perdi. Também pls preste atenção a este comentário @simov @mscdex , Espero fazê-lo recentemente, muito obrigado. :)

Eu tive o mesmo problema e atualizei meu tunnel-agent / index.js com o código de @ mike442144 de seu PR e ele corrigiu o problema. Já se passaram quase 4 anos desde que esse bug foi exposto e teve uma possível correção. Eu gostaria de saber se há alguma atualização oficial do @mikeal sobre esse problema?

Por favor, reabra este problema, mesmo npm-shrinkwrap está incluído no projeto, não funcionou agora.

Esta página foi útil?
0 / 5 - 0 avaliações