Dart HttpServer исчерпывает пространство кучи

Я модифицировал пример кода для использования на веб-сервере, таким образом, я могу запустить дротик как на сервере, так и на клиенте. Однако я решил, что хочу проверить производительность веб-сервера, и я в основном впечатлен, за исключением сбоя. Я использую пакет «siege» в Ubuntu, чтобы генерировать много трафика на веб-сайт, используя несколько URL-адресов.

Я видел, что он обеспечивает чуть севернее 1000 транзакций в секунду, что очень приемлемо для меня, но после двух-трех минут работы он либо дает сбой, либо зависает (если я увеличу new_gen_heap_size).

#import('dart:io');

class FCServer {

String basePath;

void send404(HttpResponse response)
{
    response.statusCode = HttpStatus.NOT_FOUND;
    response.outputStream.close();
}

void handleRequest(HttpRequest request, HttpResponse response)
{
  final String path = request.path == '/' ? '/index.html' : request.path;
  final File file = new File('${basePath}${path}');
  file.exists().then((bool found) {
      if (found)
      {
          file.fullPath().then((String fullPath) {
              if (!fullPath.startsWith(basePath))
              {
                  send404(response);
              }
              else
              {
                  //print("delivering $fullPath");
                  response.headers.add("Cache-Control", "max-age=3600");
                  //file.openInputStream().pipe(response.outputStream);
                  var file = new File("$fullPath");
                  //response.headers.set(HttpHeaders.CONTENT_TYPE, "$contentType; charset=UTF-8");
                  response.outputStream.write(file.readAsBytesSync());
                  response.outputStream.close();
              }
         });
        }
        else
        {
            send404(response);
        }
    }); 
}

void startServer(String basePath)
{
    this.basePath = basePath;
    var server = new HttpServer();
    print("Starting server with basePath: $basePath");
    server.listen('192.168.0.14', 8080);
    server.listen('127.0.0.1', 8080);
    server.defaultRequestHandler = handleRequest;
}
}

FCServer webServe;
main()
{
    // Compute base path for the request based on the location of the
    // script and then start the server.
    webServe = new FCServer();
    File script = new File(new Options().script);
    script.directory().then((Directory d) {
        webServe.startServer(d.path);
    });
}

Ошибка, которую Dart печатает перед выходом, выглядит следующим образом:

Exhausted heap space, trying to allocate 128096 bytes.
Exhausted heap space, trying to allocate 112 bytes.
Exception 'Instance of 'OutOfMemoryException'' thrown:
Exiting the process

Значит ли это, что где-то есть утечка памяти? Или кто-нибудь может объяснить, что происходит?

РЕДАКТИРОВАТЬ: когда new_gen_heap_size увеличивается до 1024, через некоторое время он зависает, и один поток находится на 100%, независимо от того, поступают ли запросы или нет. На данный момент объем ОЗУ составляет до 1,5 ГБ после прогона с вышеупомянутым размером кучи, поэтому я предполагаю, что сборщик мусора, так сказать, выкинул ведро.

EDIT 2: я изменил код так, что при инициализации он создает список, состоящий из 4 байтов, затем каждый раз, когда делается запрос, он просто записывает этот список в ответ и закрывает ответ. Использование памяти по-прежнему быстро растет, что указывает на более глубокую проблему в Dart. Это утомляет меня от использования Dart для полномасштабного проекта.


person coder543    schedule 02.11.2012    source источник
comment
Это утомляет меня от использования Dart для полномасштабного проекта. В каждом проекте есть ошибки (если только ваша фамилия не Кнут). До сих пор в Dart мы уделяли гораздо больше внимания коду на стороне клиента, чем коду на стороне сервера. Серверная часть начнет улучшаться по мере того, как клиентская сторона начнет остывать. Еще раз спасибо за отчет об ошибке!   -  person Shannon -jj Behrens    schedule 07.11.2012


Ответы (1)


Почему вы закомментировали "file.openInputStream().pipe(response.outputStream);" и замените его на «response.outputStream.write(file.readAsBytesSync());»? С этим есть три проблемы:

  • Он не закрывает файл, когда закончит чтение.
  • Это не асинхронно.
  • Он считывает все байты одновременно, а не передает их клиенту.

Если вам случится обслуживать действительно большой файл и у вас будет очень большое количество одновременных запросов, у вас будет много копий этого файла в памяти.

Использует ли "file.openInputStream().pipe(response.outputStream);" чтобы проблема исчезла?

Я уверен, что "response.outputStream.write(file.readAsBytesSync());" это проблема. Также помните, что .write не записывает байты сразу. Он буферизует их для записи. Следовательно, у вас есть копия всего файла, буферизованная для записи.

Обновлено: Мэдс Агер подтвердил, что это была утечка памяти, и уже отправил исправление. Вот ошибка, которую он зарегистрировал.

person Shannon -jj Behrens    schedule 02.11.2012
comment
Почему бы ему не закрыть файл, чтобы сделать это? Я проверил, и кажется, что file.close() не существует, поэтому он должен обрабатываться автоматически? И я закомментировал это по причинам, которые я не помню. Сейчас тестирую ваше решение. - person coder543; 03.11.2012
comment
Проверил, проблема не решается. Dart использовал 1,5 ГБ ОЗУ после передачи всего 25 МБ на 106 000 запросов, сгенерированных за 104 секунды. Это обслуживает простой HTML-файл, который не очень большой. - person coder543; 03.11.2012
comment
Я уверен, что легко сделать DoS против сервера, если вы просто открываете кучу запросов и никогда ничего не читаете, или если вы отдаете предпочтение открытию новых запросов, а не чтению материала. Я не думаю, что сервер еще защищен от такого рода злоупотреблений. Можно ли установить ограничение в 500 одновременных транзакций? Вы также пробовали ab (скамья Apache; она поставляется с Apache)? Возможно, пришло время сообщить об ошибке на dartbug.com. - person Shannon -jj Behrens; 06.11.2012
comment
coder543, можете ли вы проверить этот ответ зеленым? Спасибо! - person Shannon -jj Behrens; 07.11.2012
comment
Я ни в коем случае не проводил DoS-атаку, у меня было около 100 одновременных пользователей, и каждый запрос был полностью выполнен. Никакой атаки не было, это был просто ориентир. Однако, поскольку вы обновили ссылку на отчет об ошибке, я отмечу ваш ответ как правильный, поскольку, хотя это и не решение, он подтверждает проблему. - person coder543; 07.11.2012