Повысьте проблему с сервером Asio.случайное блокирование сокета-получателя при использовании пула - PullRequest
0 голосов
/ 17 апреля 2019

Я видел похожий пост здесь со схожей проблемой, но мой немного другой, и никакие ответы не помогли мне решить эту проблему. У меня есть многопоточный буст-сервер asio async HTTP, основанный на этом примере буста http / server3 . Я использую boost 1.64.0 и тестирую его на Windows 10 64 бит. Компиляция на VS 2015 64

У меня есть пул потоков, которые вызывают один экземпляр io_service.run (). Основная идея с сервером заключается в том, что у меня есть пул потоков, которые все прослушивают входящие запросы. Когда я использую один поток, все работает отлично. Веб-страница загружается без проблем. но проблема возникает, когда я использую 2+ темы. Если один поток не завершил свою работу (например, занят каким-либо POST-запросом, выполняющим некоторую работу с базой данных), сокет-получатель (случайным образом) не принимает некоторые запросы и ожидает завершения первого потока (запрос отображается как в ожидании на вкладке Сеть в Firefox). Иногда страница загружается без проблем, но чаще всего она останавливается, пока первый поток не завершит работу. Кто-нибудь знает, что может вызвать эту проблему?

Я пытался использовать определение BOOST_ASIO_ENABLE_HANDLER_TRACKING, чтобы отследить проблему, вот как она выглядит до остановки

@asio|1555450197.805179|3404*3407|strand@0000024EAAB649E0.dispatch
@asio|1555450197.805179|>3407|
@asio|1555450197.805179|3407*3408|socket@0000024EAAD400A0.async_send
@asio|1555450197.806177|<3407|
@asio|1555450197.806177|<3404|
@asio|1555450197.806177|>3405|ec=system:0
@asio|1555450197.806177|3405*3409|socket@0000024EAAB76D20.async_receive
accepting request
@asio|1555450197.806177|3405*3410|socket@0000024EA92D9CF0.async_accept
@asio|1555450197.806177|<3405|
@asio|1555450197.806177|>3408|ec=system:0,bytes_transferred=24530
@asio|1555450197.806177|3408*3411|strand@0000024EAAB649E0.dispatch
@asio|1555450197.806177|>3411|
@asio|1555450197.806177|<3411|
@asio|1555450197.806177|0|socket@0000024EAAD400A0.close
@asio|1555450197.806177|<3408|
@asio|1555450197.806177|>3409|ec=system:0,bytes_transferred=375
@asio|1555450197.807174|3409*3412|strand@0000024EAAB64580.dispatch
@asio|1555450197.807174|>3412|
@asio|1555450197.807174|3412*3413|socket@0000024EAAB76D20.async_send
@asio|1555450197.807174|<3412|
@asio|1555450197.807174|<3409|
@asio|1555450197.807174|>3413|ec=system:0,bytes_transferred=26952
@asio|1555450197.807174|3413*3414|strand@0000024EAAB64580.dispatch
@asio|1555450197.807174|>3414|
@asio|1555450197.807174|<3414|
@asio|1555450197.807174|0|socket@0000024EAAB76D20.close
@asio|1555450197.808173|<3413|

вот мой конструктор

      boost::asio::ip::tcp::resolver resolver(io_service_); 
      boost::asio::ip::tcp::resolver::query query(address, port);
      boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); 



      acceptor_.open(endpoint.protocol()); 
      acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
      acceptor_.bind(endpoint); 

      acceptor_.listen(); 

      boost::system::error_code error;
      handle_accept(error);

моя ручка принимает функцию

void server::handle_accept(const boost::system::error_code& e)
{
    if (!e)
    {

        new_connection_.reset(new connection(io_service_, *request_handler_, max_cont_lenght, min_upload_speed));

        acceptor_.async_accept(new_connection_->socket(), [this](boost::system::error_code error)
        {
            this->start_accept(error);

        });


    }
}

функция начала приема

void server::start_accept(const boost::system::error_code& e)
{
    if (!e)
    {
        new_connection_->start();
    }

    handle_accept(e);
} 

функция запуска сервера

void server::run()
{
  // Create a pool of threads to run all of the io_services.
  for (std::size_t i = 0; i < thread_pool_size_; ++i)
  {

      std::shared_ptr<boost::thread> thread(new boost::thread(
         [this](){
          this->io_service_run();
      }
      ));

    threads.push_back(thread);
  }

  // Wait for all threads in the pool to exit.
  for (std::size_t i = 0; i < threads.size(); ++i)
  {
    threads[i]->join();
  }
}

1 Ответ

0 голосов
/ 17 апреля 2019

Простой грубой силой я выяснил причину этой проблемы. видимо это не проблема с акцептором, проблема была в классе соединения. в том же http / server3 примере. Это происходит только когда вы оборачиваете свою функцию обратного вызова в цепочку.

  socket_.async_read_some(boost::asio::buffer(buffer_),
      strand_.wrap(
        boost::bind(&connection::handle_read, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred)));

Интересно, что та же самая цепь, используемая в функции handle_read, похоже, не вызывает этой проблемы. Основываясь на документации, реализация http / server3 верна

Явная цепочка является экземпляром io_service :: strand. Все объекты функций обработчика событий должны быть упакованы с помощью io_service :: strand :: wrap () или иным образом отправлены / отправлены через объект io_service :: strand. В случае составных асинхронных операций, таких как async_read () или async_read_until (), если обработчик завершения проходит через цепочку, то все промежуточные обработчики также должны проходить через одну цепочку. Это необходимо для обеспечения потокового доступа для любых объектов, которые совместно используются вызывающей стороной и составной операцией

но удаление strand_.wrap из функции start () соединения, кажется, решает проблему Не уверен, что это ошибка в boost 1.64 или я что-то упустил.

...