изящно завершить (многопоточную) службу gSOAP с включенным http-keepalive - PullRequest
2 голосов
/ 05 октября 2011

У меня многопоточная служба gSOAP, запущенная с включенным http-keepalive.Как я могу корректно завершить работу службы, когда еще есть подключенные клиенты?

Аналогичный вопрос был задан в gSoap: как корректно завершить работу приложения веб-службы? , но ответы не охватываютаспект http-keepalive: функция soap-serve просто не вернется, пока клиент не закроет http-keepalive-session.Таким образом, шаг 2 в принятом ответе будет блокироваться до тех пор, пока клиент не решит закрыть соединение (или не истечет время ожидания приема, но короткий таймаут нарушит желаемое поведение http-keepalive здесь).

Примеры издокументация по gSOAP страдает той же проблемой.

До сих пор я пытался вызвать soap_done () для всех структур soap, которые висят в вызове soap_serve из основного потока, чтобы прервать соединения, ожидающие http-keepalive., которая работает большую часть времени, но дает сбой в редких условиях (возможно, в состоянии гонки), поэтому для меня это не решение.

1 Ответ

0 голосов
/ 09 ноября 2011

Я только что столкнулся с той же самой проблемой, и я думаю, что у меня есть решение для вас.

Как вы только что сказали, проблема в том, что gSoap висит на soap_serve. Это происходит из-за того, что gSOAP генерирует для вас внутренний цикл, который ожидает прибытия всех запросов поддержки активности ИЛИ тайм-аут на стороне сервера.

Я взял функцию soap_serve внутри автоматически сгенерированной заглушки. Я собираюсь перечислить оригинальную функцию soap_serve, чтобы вы могли найти ее в служебном файле-заглушке:

  SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap)
    {
    #ifndef WITH_FASTCGI
        unsigned int k = soap->max_keep_alive;
    #endif

        do
        {
    #ifdef WITH_FASTCGI
            if (FCGI_Accept() < 0)
            {
                soap->error = SOAP_EOF;
                return soap_send_fault(soap);
            }
    #endif

            soap_begin(soap);

    #ifndef WITH_FASTCGI
            if (soap->max_keep_alive > 0 && !--k)
                soap->keep_alive = 0;
    #endif

            if (soap_begin_recv(soap))
            {   if (soap->error < SOAP_STOP)
                {
    #ifdef WITH_FASTCGI
                    soap_send_fault(soap);
    #else 
                    return soap_send_fault(soap);
    #endif
                }
                soap_closesock(soap);

                continue;
            }

            if (soap_envelope_begin_in(soap)
             || soap_recv_header(soap)
             || soap_body_begin_in(soap)
             || soap_serve_request(soap)
             || (soap->fserveloop && soap->fserveloop(soap)))
            {
    #ifdef WITH_FASTCGI
                soap_send_fault(soap);
    #else
                return soap_send_fault(soap);
    #endif
            }

    #ifdef WITH_FASTCGI
            soap_destroy(soap);
            soap_end(soap);
        } while (1);
    #else
        } while (soap->keep_alive);
    #endif
        return SOAP_OK;
    }

Вы должны извлечь тело этой функции и заменить ваш старый вызов soap_serve (mySoap) внутри вашего потока (потока, выполняющего запросы и ошибки из-за keep-alive) следующим:

do
    {
        if ( Server::mustShutdown() ) {
            break;
        }

        soap_begin(mySoap);

        // If we reached the max_keep_alive we'll exit
        if (mySoap->max_keep_alive > 0 && !--k)
            mySoap->keep_alive = 0;


        if (soap_begin_recv(mySoap))
        {   if (mySoap->error < SOAP_STOP)
            {
                soap_send_fault(mySoap);
                break;
            }
            soap_closesock(mySoap);

            continue;
        }

        if (soap_envelope_begin_in(mySoap)
         || soap_recv_header(mySoap)
         || soap_body_begin_in(mySoap)
         || soap_serve_request(mySoap)
         || (mySoap->fserveloop && mParm_Soap->fserveloop(mySoap)))
        {
            soap_send_fault(mySoap);
            break;
        }


    } while (mySoap->keep_alive);

Обратите внимание на следующее:

  1. Server :: mustShutdown () действует как флаг, который будет установлен в значение true (внешне), чтобы завершить все потоки. Когда вы захотите остановить обработку сервером новых запросов, эта функция вернет true и цикл завершится.
  2. Я удалил ifdef, WITH_FASTCGI нам сейчас не интересно.
  3. Когда вы закрываете соединение, как это, любые клиенты, подключенные к серверу, вызовут исключение. Например, клиенты, написанные на C #, выдают «Основное соединение исключено, чтобы сервер оставался живым», что имеет для нас смысл.

Но мы еще не закончили, благодаря тому, что указал AudioComplex, система все еще ожидает запросов на soap_begin_recv. Но у меня тоже есть решение;)

Каждый из потоков в пуле обработки соединений создает копию основного мыльного контекста (через soap_copy), именно эти потоки
Я храню каждый из этих контекстов как элемент массива, который находится в главном потоке обработки соединений. При завершении основного потока обработки соединения (тот, который обслуживает запросы) он пройдет через все мыльные контексты и завершит «ручное» соединение, используя:

for (int i = 0; i < soaps.size(); ++i) {
  soaps[i]->fclose(soaps[i]);
}

Это принудительно завершит цикл soap_serve. Это фактически остановит внутренний цикл возле линии 921 stdsoap2.cpp_

r = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout);

Это не самое чистое решение (не нашло более чистого), но оно определенно остановит обслуживание.

...