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.
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.
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. :)