【问题标题】:PHP SoapClient TimeoutPHP SoapClient 超时
【发布时间】:2011-03-30 20:31:36
【问题描述】:

SoapClient 请求是否会超时并引发异常。截至目前,我得到 PHP 服务器响应超时,在我的情况下为 60 秒。基本上我想要的是,如果在特定时间内没有来自 Web 服务的任何回复,则会引发异常并且我可以捕获它。 60 秒的警告不是我想要的。

【问题讨论】:

    标签: php web-services timeout soap-client


    【解决方案1】:

    无效答案。请改用https://stackoverflow.com/a/12119215/441739


    虽然 Andrei 链接到了一个不错的解决方案,但这个解决方案的代码更少,但却得到了一个很好的解决方案:

    * Handling Timeouts with PHP5 SoapClient Extension (by Antonio Ramirez; 02 Feb 2010)
    示例代码:
    //
    // 设置连接超时(示例中为 15 秒)
    //
    $client = new SoapClient($wsdl, array("connection_timeout" => 15));
    如果您需要更细粒度的 HTTP 控制,还有流上下文。参见new SoapClient()Docsstream_context 选项。在表面之下SoapClient 使用 HTTP 和 SSL 传输。

    【讨论】:

    • 当我使用这个时,我得到Unknown SOAP client option Exception
    • @DanFromGermany,你运行的是什么版本的 PHP?除了上面显示的内容之外,您是否传递了任何选项?
    • PHP 5.3,我也通过了'soap_version' => SOAP_1_1,,但我使用的是Zend_Soap_Client,但它只是扩展了SoapClient
    • @DanFromGermany,实际上 Zend_Soap_Client 包装了 SoapClient,而不是扩展它。您可以在此处查看 Zend_Soap_Client 支持的选项列表:github.com/zendframework/zf2/blob/master/library/Zend/Soap/… connection_timeout 不是受支持的选项。
    • 根据 php 文档,此超时仅影响到服务的连接,而不影响操作实际花费的时间。您需要像下面提到的那样设置default_socket_timeout
    【解决方案2】:
    ini_set("default_socket_timeout", 15);
    $client = new SoapClient($wsdl, array(......));
    

    connection_timeout 选项为 SOAP 服务的连接。此选项未定义超时 用于响应缓慢的服务。限制等待呼叫的时间 完成 default_socket_timeout 设置可用。

    【讨论】:

    • 这就是答案。 :)
    • 只要不超过套接字超时,这是一个很好的答案。否则服务器会抛出一个连接超时并且执行会停止...
    • 另外请注意,还有这个未解决的 PHP 错误:bugs.php.net/bug.php?id=41631。所以default_socket_timeout 可能不适用于 HTTPS 连接
    • default_socket_timeout 适用于响应缓慢的服务。问题是关于处理没有响应的服务。
    • @T30 - 是一回事。无论哪种情况,此设置都会在 X 秒后终止请求。
    【解决方案3】:

    看看

    如果你觉得舒服并且你的环境允许你扩展类。

    它基本上扩展了SoapClient 类,将HTTP 传输替换为可以处理超时的curl:

    class SoapClientTimeout extends SoapClient
    {
        private $timeout;
    
        public function __setTimeout($timeout)
        {
            if (!is_int($timeout) && !is_null($timeout))
            {
                throw new Exception("Invalid timeout value");
            }
    
            $this->timeout = $timeout;
        }
    
        public function __doRequest($request, $location, $action, $version, $one_way = FALSE)
        {
            if (!$this->timeout)
            {
                // Call via parent because we require no timeout
                $response = parent::__doRequest($request, $location, $action, $version, $one_way);
            }
            else
            {
                // Call via Curl and use the timeout
                $curl = curl_init($location);
    
                curl_setopt($curl, CURLOPT_VERBOSE, FALSE);
                curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
                curl_setopt($curl, CURLOPT_POST, TRUE);
                curl_setopt($curl, CURLOPT_POSTFIELDS, $request);
                curl_setopt($curl, CURLOPT_HEADER, FALSE);
                curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-Type: text/xml"));
                curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeout);
    
                $response = curl_exec($curl);
    
                if (curl_errno($curl))
                {
                    throw new Exception(curl_error($curl));
                }
    
                curl_close($curl);
            }
    
            // Return?
            if (!$one_way)
            {
                return ($response);
            }
        }
    }
    

    【讨论】:

    • 谢谢。那将是一个可以看看的地方。让我们看看还有没有其他的。
    • 我已经在 PHP 5.4.6 中尝试过这个,但似乎他们已经更改了 _doRequest() 的签名,没有另行通知。 $request 不再包含原始 XML,而是像 141201299690460051141201717499383133141201717499423132141201717499443131141201717499463131141201717499483135false 这样的字符串。任何人都可以对此有所了解吗?
    • 这会破坏 SoapClient 的整个内部结构。
    • @MichaelHärtl 你能提供更多信息吗?这可能是个大问题,但我很难重现它。
    • @Nanne 不幸的是没有。我不确定是什么导致了我遇到的问题以及我为解决它所做的更改。但是通过查看我的代码,我最终使用了与此处描述的解决方案类似的方法并且它有效。但也许我只是升级到比 5.4.6 更高的 PHP 版本。不记得了。
    【解决方案4】:

    接受的答案将破坏 SoapClient 必须提供的所有功能。比如设置正确的内容标题、身份验证等。

    这将是解决问题的更好方法

    class MySoapClient extends \SoapClient
    {
        private $timeout = 10;
    
        public function __construct($wsdl, array $options)
        {
            // Defines a timeout in seconds for the connection to the SOAP service.
            // This option does not define a timeout for services with slow responses.
            // To limit the time to wait for calls to finish the default_socket_timeout setting is available.
            if (!isset($options['connection_timeout'])) {
                $options['connection_timeout'] = $this->timeout;
            }
    
            parent::__construct($wsdl, $options);
        }
    
        public function setTimeout($timeout)
        {
            $this->timeout = $timeout;
        }
    
        public function __doRequest($request, $location, $action, $version, $one_way = 0)
        {
            $original = ini_get('default_socket_timeout');
            ini_set('default_socket_timeout', $this->timeout);
            $response = parent::__doRequest($request, $location, $action, $version, $one_way);
            ini_set('default_socket_timeout', $original);
    
            return $response;
        }
    
    }
    

    【讨论】:

    • 为了获得额外的安全网,您应该尝试/捕获父调用(例如,某些应用程序/框架可能会通过自定义 error_handler 抛出警告异常),否则会留下未重置的default_socket_timeout跨度>
    • connection_timeout 本身的默认值应该比 10 秒小很多。
    【解决方案5】:

    你可以通过 composer 安装它: https://github.com/ideaconnect/idct-soap-client

    它扩展了标准 SoapClient 并提供了设置重试次数、连接和读取超时的选项。

    【讨论】:

      【解决方案6】:

      我在使用 SOAPClient 时使用以下逻辑:

      public function executeSoapCall($method, $params)
      {
          try {
              $client = $this->tryGetSoapClient();
      
              $timeout = ini_get('default_socket_timeout');
              ini_set('default_socket_timeout', 60);//set new timeout value - 60 seconds
              $client->__soapCall($method, $params);//execute SOAP call
              ini_set('default_socket_timeout', $timeout);//revert timeout back
          } catch (\Throwable $e) {
              if (isset($timeout)) {
                  ini_set('default_socket_timeout', $timeout);//revert timeout back
              }
          }
      }
      protected function tryGetSoapClient()
      {
          $timeout = ini_get('default_socket_timeout');//get timeout (need to be reverted back afterwards)
          ini_set('default_socket_timeout', 10);//set new timeout value - 10 seconds
          try {
              $client = new \SoapClient($this->wsdl, $this->options);//get SOAP client
          } catch (\Throwable $e) {
              ini_set('default_socket_timeout', 10);//revert back in case of exception
              throw $e;
          }
          $this->iniSetTimeout($timeout);//revert back
      
          return $client;
      }
      

      这可以帮助我等待长达 10 秒的连接建立和 60 秒的调用执行时间。

      【讨论】:

        【解决方案7】:

        您也可以使用stream_context_create() 并将timeout 选项添加到http 数组中:

        $context = stream_context_create(
            array(
                'http' => array(
                    "timeout" => 10,
                ),
            )
        );
        

        Here is the PHP manual page

        SoapHandler 的初始化应该是:

        $soapHandler = new SoapClient($wsdl, [
            //more params, if needed..
            
            'stream_context' => $context,
        ]);
        

        【讨论】:

        • 为我工作。谢谢
        猜你喜欢
        • 2012-02-25
        • 2014-07-09
        • 1970-01-01
        • 2015-01-13
        • 2021-10-08
        • 1970-01-01
        • 2015-10-14
        • 2012-10-06
        相关资源
        最近更新 更多