【问题标题】:gracefully shutdown (multi-threaded) gSOAP service with http-keepalive enabled在启用 http-keepalive 的情况下正常关闭(多线程)gSOAP 服务
【发布时间】:2021-07-28 13:37:16
【问题描述】:

我有一个启用了 http-keepalive 的多线程 gSOAP 服务。当仍有客户端连接时,如何优雅地关闭服务?

gSoap: how to gracefully shutdown the webservice application? 中提出了类似的问题,但答案并未涵盖 http-keepalive 方面:soap-serve 函数将不会返回,直到客户端未关闭 http-keepalive-session。因此,接受的答案中的第 2 步将阻塞,直到客户端决定关闭连接(或接收超时到期,但短暂的超时会破坏此处所需的 http-keepalive 行为)。

gSOAP 文档中的示例遇到了同样的问题。

到目前为止,我尝试的是为挂在主线程的soap_serve调用中的所有soap结构调用soap_done(),以中断等待http-keepalive的连接,这在大多数情况下都有效,但很少发生崩溃条件(可能是竞争条件),所以这对我来说不是解决方案。

【问题讨论】:

    标签: gsoap keep-alive


    【解决方案1】:

    我刚刚遇到了同样的问题,我想我已经为你找到了解决方案。

    正如您刚才所说,问题在于 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) 调用替换为以下内容:

    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 并且循环将结束。
    2. 我已经删除了 ifdef,WITH_FASTCGI 现在我们不感兴趣了。
    3. 当您像这样关闭连接时,任何连接到服务器的客户端都会引发异常。例如,用 C# 编写的客户端会抛出“底层连接已被服务器关闭以保持活动状态”,这对我们来说非常有意义。

    但是我们还没有完成,感谢AudioComplex 指出的,系统仍然在等待soap_begin_recv 上的reqeuests。但我也有解决方案;)

    连接处理池上的每个线程都会创建主soap上下文的副本(通过soap_copy),这些线程是
    我将这些上下文中的每一个都存储为驻留在主连接处理线程上的数组上的一个元素。 当终止主连接处理线程(为请求提供服务的线程)时,它将遍历所有soap上下文并使用以下方法“手动”完成连接:

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

    这将强制soap_serve 循环结束。它实际上会在stdsoap2.cpp_的第921行附近停止内部循环_

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

    这不是最干净的解决方案(还没有找到更干净的解决方案),但它肯定会停止服务。

    【讨论】:

    • 首先感谢您的回复!但我认为这不会解决我的问题。在您的解决方案中,soap_begin_recv() 调用仍然会阻塞,直到发生超时或客户端连接到套接字,对吧?
    • 您好,刚刚在帖子中回答了您的问题,希望对您有所帮助!
    • 谢谢,确实解决了问题。非常感谢您的详细解决方案! (抱歉,我很久没看这个帖子了……)
    【解决方案2】:

    您不需要更改soap_serve中的循环,只需在您的服务实现中返回一些错误代码:

    return Server::mustShutdown() ? SOAP_SVR_FAULT : SOAP_OK;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      • 1970-01-01
      • 2018-01-07
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      相关资源
      最近更新 更多