YAWS отвечает ошибками при небольшой нагрузке. Необработанный ответ фр. do_recv() {ошибка, ошибка}

Здравствуйте, эрлангеры :)

Просто еще один энтузиаст Erlang, играющий с языком здесь. У меня есть очень простой модуль приложения YAWS, который отлично работает при доступе к отдельным клиентам. Однако, если я попытаюсь объединить несколько клиентов для имитации легкого трафика, эти клиенты начнут получать ошибки. Любая идея, что может быть причиной этого?

Код, порождающий клиентов:

-module(concurrent).
-export([measure/0, get/0]).

get() ->
  Result = httpc:request("http://localhost:8080/blah"),
  io:format("Result: ~p.\n", [Result]).

get_spawner(0) -> io:format("Done.\n");
get_spawner(Times) ->
  spawn(concurrent, get, []),
  get_spawner(Times - 1).

measure() ->
  io:fwrite("Starting inets...\n"),
  inets:start(),
  io:fwrite("Done\n"),

  io:fwrite("Creating blah...\n"),
  httpc:request(put, { "http://localhost:8080/blah", [], "", "" }, [], []),
  io:fwrite("Done\n"),

  get_spawner(9),

  io:fwrite("Stopping inets...\n"),
  inets:stop(),
  io:fwrite("Done\n"),

  init:stop().

Я заметил, что все это прекрасно работает для 8 одновременных клиентов, но когда я объединяю 9 или более клиентов, они начинают получать от сервера следующее сообщение:

=ERROR REPORT==== 24-Jan-2017::12:30:04 ===
** Generic server httpc_manager terminating
** Last message in was {request,
                           {request,undefined,<0.74.0>,0,http,
                               {"localhost",8080},
                               "/blah",[],get,
                               {http_request_h,undefined,"keep-alive",
                                   undefined,undefined,undefined,undefined,
                                   undefined,undefined,undefined,undefined,
                                   undefined,undefined,undefined,undefined,
                                   undefined,undefined,"localhost:8080",
                                   undefined,undefined,undefined,undefined,
                                   undefined,undefined,undefined,undefined,
                                   undefined,[],undefined,undefined,undefined,
                                   undefined,"0",undefined,undefined,
                                   undefined,undefined,undefined,undefined,[]},
                               {[],[]},
                               {http_options,"HTTP/1.1",infinity,true,
                                   {essl,[]},
                                   undefined,false,infinity,false},
                               "http://localhost:8080/blah",[],none,[],
                               1485261004189,undefined,undefined,false}}
** When Server state == {state,[],httpc_manager__handler_db,
                            {cookie_db,undefined,8209},
                            httpc_manager__session_db,httpc_manager,
                            {options,
                                {undefined,[]},
                                {undefined,[]},
                                0,2,5,120000,2,disabled,false,inet,default,
                                default,[]}}
** Reason for termination ==
** {'EXIT',
       {shutdown,
           {gen_server,call,
               [httpc_handler_sup,
                {start_child,
                    [<0.61.0>,
                     {request,#Ref<0.0.6.64>,<0.74.0>,0,http,
                         {"localhost",8080},
                         "/blah",[],get,
                         {http_request_h,undefined,"keep-alive",undefined,
                             undefined,undefined,undefined,undefined,
                             undefined,undefined,undefined,undefined,
                             undefined,undefined,undefined,undefined,
                             undefined,"localhost:8080",undefined,undefined,
                             undefined,undefined,undefined,undefined,
                             undefined,undefined,undefined,[],undefined,
                             undefined,undefined,undefined,"0",undefined,
                             undefined,undefined,undefined,undefined,
                             undefined,[]},
                         {[],[]},
                         {http_options,"HTTP/1.1",infinity,true,
                             {essl,[]},
                             undefined,false,infinity,false},
                         "http://localhost:8080/blah",[],none,[],
                         1485261004189,undefined,undefined,false},
                     {options,
                         {undefined,[]},
                         {undefined,[]},
                         0,2,5,120000,2,disabled,false,inet,default,default,
                         []},
                     httpc_manager]},
                infinity]}}}

=ERROR REPORT==== 24-Jan-2017::12:30:04 ===
Error in process <0.74.0> with exit value:
{{case_clause,
     {undefined,
         {error,
             {'EXIT',
                 {shutdown,
                     {gen_server,call,
                         [httpc_handler_sup,
                          {start_child,
                              [<0.61.0>,
                               {request,#Ref<0.0.6.64>,<0.74.0>,0,http,
                                   {"localhost",8080},
                                   "/blah",[],get,
                                   {http_request_h,undefined,"keep-alive",
                                       undefined,undefined,undefined,
                                       undefined,undefined,undefined,
                                       undefined,undefined,undefined,
                                       undefined,undefined,undefined,
                                       undefined,undefined,"localhost:8080",
                                       undefined,undefined,undefined,
                                       undefined,undefined,undefined,
                                       undefined,undefined,undefined,[],
                                       undefined,undefined,undefined,
                                       undefined,"0",undefined,undefined,
                                       undefined,undefined,undefined,
                                       undefined,[]},
                                   {[],[]},
                                   {http_options,"HTTP/1.1",infinity,true,
                                       {essl,[]},
                                       undefined,false,infinity,false},
                                   "http://localhost:8080/blah",[],none,[],
                                   1485261004189,undefined,undefined,false},
                               {options,
                                   {undefined,[]},
                                   {undefined,[]},
                                   0,2,5,120000,2,disabled,false,inet,default,
                                   default,[]},
                               httpc_manager]},
                          infinity]}}}}}},
 [{httpc,handle_request,9,[{file,"httpc.erl"},{line,574}]},
  {concurrent,get,0,
      [{file,
           "c:/Users/piotr_justyna/Documents/rets/performance_tests/concurrent.erl"},
       {line,5}]}]}

На стороне сервера получаю следующее:

=ERROR REPORT==== 24-Jan-2017::12:28:55 ===
Unhandled reply fr. do_recv() {error,econnaborted}

Я уверен, что это что-то простое, просто не могу понять, что это такое. Является ли это причиной количества одновременных подключений? Можете ли вы помочь пролить больше света на это?

С уважением, Петр


person Piotr Justyna    schedule 24.01.2017    source источник


Ответы (1)


Ваш код порождает несколько вызовов httpc:request/1, но сразу после этого останавливается inets:

...
get_spawner(9),
io:fwrite("Stopping inets...\n"),
inets:stop(),
...

Поскольку inets закрыт, запросы, которые все еще выполняются, закрываются преждевременно, как вы можете видеть в отчете об ошибке:

** Reason for termination ==
** {'EXIT',
       {shutdown,
           {gen_server,call,
               [httpc_handler_sup,

Вам нужно заставить родителя всех порожденных запросов ждать завершения этих запросов перед вызовом inets:stop/0. Один из способов сделать это — передать родительский pid каждому порожденному дочернему элементу, и как только httpc:request/1 вернется, дочерний элемент отправит сообщение родителю. Родитель порождал бы все N дочерних элементов, затем сидел в цикле, ожидая получения N сообщений о завершении от дочерних элементов, и только после получения всех сообщений о завершении он вызывал inets:stop/0.

Причина, по которой он работает для 8 запросов, но не для 9, связана с состоянием гонки из-за отсутствия ожидания перед вызовом inets:stop/0. По умолчанию размер пула процессов-приемщиков Yaws, обрабатывающих запросы, равен 8. Когда ваш код отправляет 8 запросов, пул обрабатывает их достаточно быстро, чтобы превзойти вызов вашего кода на inets:stop/0, но с 9 возникает небольшая задержка, так как обрабатывается один акцептор. два запроса, и этого небольшого изменения времени достаточно, чтобы ваш вызов inets:stop/0 заблокировал один или несколько ваших клиентских процессов. Вы можете изменить размер пула акцепторов Yaws с помощью переменной конфигурации acceptor_pool_size.

Если вы серьезно относитесь к нагрузочному тестированию, вы можете рассмотреть возможность использования промышленного решения, такого как tsung, вместо развертывания собственного.

person Steve Vinoski    schedule 24.01.2017
comment
Фантастический. Качественная консультация, большое спасибо. Позвольте мне попробовать это, и я обновлю ветку. - person Piotr Justyna; 24.01.2017
comment
Это было именно так, большое спасибо. И спасибо, что порекомендовали tsung — похоже, я скоро начну его использовать. - person Piotr Justyna; 24.01.2017