Что такое зависание сокета? Что случается?

задняя часть HTTP
Что такое зависание сокета? Что случается?

Человек с чистым сердцем живет жизнью, полной сладости и радости. -- Лев Толстой

Что касается зависания сокета, то оно впервые появилось в стресс-тесте сервиса, а потом было решено.Недавно об этой проблеме сообщалось при миграции сервиса 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'
}