Человек с чистым сердцем живет жизнью, полной сладости и радости. -- Лев Толстой
Что касается зависания сокета, то оно впервые появилось в стресс-тесте сервиса, а потом было решено.Недавно об этой проблеме сообщалось при миграции сервиса Node.js на контейнер K8S.После проверки причины выяснилось, что это был размер процессора и памяти контейнера.Вызванные ограничениями, вот краткое изложение того, что такое зависание сокета, при каких обстоятельствах и как это решить.
об авторе: Джун Май, разработчик Nodejs, сертифицированный автор МООК, молодежь после 90-х, которая любит технологии и любит делиться, добро пожаловать на внимание.Стек технологий Nodejsи проект с открытым исходным кодом Githubwww.nodejs.red
Что такое Socket повесить трубку
Это также можно использовать в качестве вопроса на собеседовании, чтобы спросить, что такое Socket повесить трубку?
Hang up в переводе с английского означает повесить трубку, а зависание сокета можно понимать и как зависание сокета (ссылки). Независимо от того, какой язык вы используете, вы должны были встретить много или меньше, но я не знаю, задумывались ли вы когда-нибудь о том, почему? Например, тайм-аут http-сервера по умолчанию, предоставленный системой в Node.js, составляет 2 минуты (server.timeout можно посмотреть).Если запрос превышает это время, http-сервер закроет ссылку запроса.Когда клиент захочет чтобы вернуть запрос, он обнаруживает, что сокет был «завис», и будет сообщено об ошибке зависания сокета.
Чтобы понять проблему, вам все равно нужно больше практиковаться Давайте воспроизведем эту проблему из небольшой демонстрации, а затем объединим исходный код, связанный с Node.js http, чтобы узнать больше о том, что такое зависание Socket? Кроме того, я также рекомендую вам взглянуть на всемогущее переполнение стека, здесь также есть обсуждение этого вопроса.stackoverflow.com/questions/1….
Воспроизвести зависание сокета
Сервер
Запустите службу http и определите интерфейс /timeout для задержки ответа через 3 минуты.
const http = require('http');
const port = 3020;
const server = http.createServer((request, response) => {
console.log('request url: ', request.url);
if (request.url === '/timeout') {
setTimeout(function() {
response.end('OK!');
}, 1000 * 60 * 3)
}
}).listen(port);
console.log('server listening on port ', port);
клиент
const http = require('http');
const opts = {
hostname: '127.0.0.1',
port: 3020,
path: '/timeout',
method: 'GET',
};
http.get(opts, (res) => {
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
console.log(rawData);
} catch (e) {
console.error(e.message);
}
});
}).on('error', err => {
console.error(err);
});
После запуска сервера, а затем запуска клиента примерно на 2 минуты или непосредственного уничтожения сервера будет сообщено о следующей ошибке, и вы можете увидеть соответствующий стек ошибок.
Error: socket hang up
at connResetException (internal/errors.js:570:14)
at Socket.socketOnEnd (_http_client.js:440:23)
at Socket.emit (events.js:215:7)
at endReadableNT (_stream_readable.js:1183:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21) {
code: 'ECONNRESET'
}
Почему ошибка зависания сокета сообщается на стороне http-клиента? ошибка connResetException('зависание сокета) будет вызвана ошибкой L440. up').
// https://github.com/nodejs/node/blob/v12.x/lib/_http_client.js#L440
function socketOnEnd() {
const socket = this;
const req = this._httpMessage;
const parser = this.parser;
if (!req.res && !req.socket._hadError) {
// If we don't have a response then we know that the socket
// ended prematurely and we need to emit an error on the request.
req.socket._hadError = true;
req.emit('error', connResetException('socket hang up'));
}
if (parser) {
parser.finish();
freeParser(parser, req, socket);
}
socket.destroy();
}
Решение для зависания розетки
1. Установите время ожидания сокета http-сервера
См. следующий исходный код http-сервера Node.js. По умолчанию значение времени ожидания сервера составляет 2 минуты. Если время ожидания истекло, сокет будет автоматически уничтожен. Вы можете настроить время ожидания, вызвав server.setTimeout(msecs ) метод Отключить механизм тайм-аута
// https://github.com/nodejs/node/blob/v12.x/lib/_http_server.js#L348
function Server(options, requestListener) {
// ...
this.timeout = kDefaultHttpServerTimeout; // 默认为 2 * 60 * 1000
this.keepAliveTimeout = 5000;
this.maxHeadersCount = null;
this.headersTimeout = 40 * 1000; // 40 seconds
}
Object.setPrototypeOf(Server.prototype, net.Server.prototype);
Object.setPrototypeOf(Server, net.Server);
Server.prototype.setTimeout = function setTimeout(msecs, callback) {
this.timeout = msecs;
if (callback)
this.on('timeout', callback);
return this;
};
Измененный код выглядит следующим образом:
const server = http.createServer((request, response) => {
console.log('request url: ', request.url);
if (request.url === '/timeout') {
setTimeout(function() {
response.end('OK!');
}, 1000 * 60 * 3)
}
}).listen(port);
server.setTimeout(0); // 设置超时时间
Если вы не установите setTimeout, вы также можете перехватить эту ошибку на стороне http-клиента и поставить ее в очередь для инициации повторной попытки.Когда вероятность такой ошибки высока, вам нужно проверить, нет ли у соответствующей службы ненормальных проблем. например, медленная обработка.
ECONNRESET VS ETIMEDOUT
Обратите внимание на разницу между ECONNRESET и ETIMEDOUT.
ECONNRESET — тайм-аут чтения, ошибка {"code":"ECONNRESET"} возникает, когда сервер слишком медленный для правильного ответа, например, в примере с зависанием сокета, описанном выше.
ETIMEDOUT — тайм-аут ссылки, относится к тайм-ауту, который возникает, когда клиент устанавливает соединение с удаленным сервером.Ниже приведен пример запроса для модуля запроса.
const request = require('request');
request({
url: 'http://127.0.0.1:3020/timeout',
timeout: 5000,
}, (err, response, body) => {
console.log(err, body);
});
В приведенном выше примере сообщение об ошибке {code: 'ETIMEDOUT'} будет сообщено примерно через 5 секунд. Стек выглядит следующим образом:
Error: ETIMEDOUT
at Timeout._onTimeout (/Users/test/node_modules/request/request.js:677:15)
at listOnTimeout (internal/timers.js:531:17)
at processTimers (internal/timers.js:475:7) {
code: 'ETIMEDOUT'
}