Zend HttpClient更精细的超时?

时间:2014-10-05 15:14:50

标签: php curl zend-framework2

似乎ZendFramework2的各种http客户端适配器允许在秒范围内连接和读取超时;通过在设置之前将各种超时参数强制转换为(int)来强制执行。在我们的组织中,我们通常为生产环境指定较小的连接和读取超时(我们已实施SOA)。

但是,php支持(浮点)值中的Socket级别函数可以支持超时以支持亚秒级超时,并且自PHP 5.2.3起,有一些方法可以使用libcurl支持亚秒级连接超时。 (CURLOPT_CONNECTTIMEOUT_MS,cURL符合每个how enable curl's AsynchDNS?的c-ares。)

我很高兴为ZendFramework输入一张机票,以支持更小,更精细的超时,但我想首先看看这是否是一个解决的问题,在野外'。有没有人得到Zend \ Http \ Client \ Adapter \ Socket或\ Curl适配器来支持亚秒连接和读取超时?

1 个答案:

答案 0 :(得分:1)

我知道解决问题的两种方法。你可以使用它作为临时修复,直到Zend Framework 2支持它自己的亚秒超时。

  1. 覆盖Socket / Curl

    创建模块并从Socket / Curl类继承并覆盖connect()方法以支持亚秒超时。

    对于Curl类,我会在配置数组中添加一个新选项timeout_ms,因此您仍然可以使用旧的timeout选项。 我的代码仅显示超时部分的相关更改。

    <?php
    use Zend\Http\Client\Adapter\Curl;
    
    class CurlMs extends Curl 
    {
    
      public function connect($host, $port = 80, $secure = false)
      {
        ...
        // Replace the lines with timeout with the following lines.
        if (isset($this->config['timeout_ms'])) {
          // Set timeout milliseconds
          curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT_MS, $this->config['timeout_ms']);
        } elseif (isset($this->config['timeout'])) {
          // Set timeout
          curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $this->config['timeout']);
        }
        ...
      }
    }
    

    对于Socket类,由于stream_socket_client()stream_socket_timeout()的接口不同,情况有点难以解决。选项timeout现在是浮点值。 如上所述,我只包含了相关的代码部分。

    <?php
    use Zend\Http\Client\Adapter\Socket;
    
    class SocketMs extends Socket 
    {
    
      public function connectconnect($host, $port = 80, $secure = false)
      { 
       ...
        // Replace the lines with socket connect
        $this->socket = stream_socket_client(
          $host . ':' . $port,
          $errno,
          $errstr,
          (float) $this->config['timeout'],
          $flags,
          $context
        );
        ...
        // Replace the lines with stream timeout
        // get the fraction of the float value and convert it microseconds
        $fraction = ($this->config['timeout'] - floor($this->config['timeout'])) * 1000;
        // Set the stream timeout               
        if (!stream_set_timeout($this->socket, (int) $this->config['timeout'], (int) $fraction)  {                                      
          throw new AdapterException\RuntimeException('Unable to set the connection timeout');
        }
        ...
      }
    }
    
  2. 修补Socket.php / Curl.php个文件

    为要添加亚秒超时的文件编写补丁,并将其应用于ZendFramework 2版本。如果您使用composer,则可以使用ZendFramework 2 version 2.3.3自动执行该过程。我针对{{3}}创建了补丁。

    我不推荐这种方法,因为它有一些陷阱。

    Curl.php文件的补丁如下:

    --- a/library/Zend/Http/Client/Adapter/Curl.php
    +++ b/library/Zend/Http/Client/Adapter/Curl.php
    @@ -200,7 +200,10 @@ class Curl implements HttpAdapter, StreamInterface
                 curl_setopt($this->curl, CURLOPT_PORT, intval($port));
             }
    
    -        if (isset($this->config['timeout'])) {
    +        if (isset($this->config['timeout_ms'])) {
    +            // Set timeout milliseconds
    +            curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT_MS, $this->config['timeout_ms']);
    +        } elseif (isset($this->config['timeout'])) {
                 // Set timeout
                 curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $this->config['timeout']);
             }
    

    Socket.php文件的补丁如下:

    --- a/library/Zend/Http/Client/Adapter/Socket.php
    +++ b/library/Zend/Http/Client/Adapter/Socket.php
    @@ -247,7 +247,7 @@ class Socket implements HttpAdapter, StreamInterface
                     $host . ':' . $port,
                     $errno,
                     $errstr,
    -                (int) $this->config['timeout'],
    +                (float) $this->config['timeout'],
                     $flags,
                     $context
                 );
    @@ -268,7 +268,9 @@ class Socket implements HttpAdapter, StreamInterface
                 }
    
                 // Set the stream timeout
    -            if (!stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
    +            // get the fraction of the timeout and convert it to microseconds
    +            $fraction = ($this->config['timeout'] - floor($this->config['timeout'])) * 1000;
    +            if (!stream_set_timeout($this->socket, (int) $this->config['timeout'], (int) $fraction)) {
                     throw new AdapterException\RuntimeException('Unable to set the connection timeout');
                 }