stream_select新打开的套接字超时


stream_select timeout on newly opened socket

我使用thrift php客户端连接到java thrift服务器。但得到了错误

TSocket: timed out writing 78 bytes from 10.0.1.151:1234

我深入研究了节俭的 php 源代码,发现它是由函数 stream_select 超时引起的。

/**
 * Write to the socket.
 *
 * @param string $buf The data to write
 */
public function write($buf)
{
    $null = null;
    $write = array($this->handle_);
    // keep writing until all the data has been written
    while (TStringFuncFactory::create()->strlen($buf) > 0) {
        // wait for stream to become available for writing
        $writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_);
        if ($writable > 0) {
            // write buffer to stream
            $written = @stream_socket_sendto($this->handle_, $buf);
            if ($written === -1 || $written === false) {
                throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '.
                        $this->host_.':'.$this->port_);
            }
            // determine how much of the buffer is left to write
            $buf = TStringFuncFactory::create()->substr($buf, $written);
        } elseif ($writable === 0) {
            throw new TTransportException('TSocket: timed out writing '.TStringFuncFactory::create()->strlen($buf).' bytes from '.
                    $this->host_.':'.$this->port_);
        } else {
            throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '.
                    $this->host_.':'.$this->port_);
        }
    }
}

这意味着套接字被阻塞并且无法写入。但是套接字只是打开的,不应该被阻塞。我尝试在pfsockopen后立即选择

if ($this->persist_) {
  $this->handle_ = @pfsockopen($this->host_,
                               $this->port_,
                               $errno,
                               $errstr,
                               $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000));
} else {
  $this->handle_ = @fsockopen($this->host_,
                              $this->port_,
                              $errno,
                              $errstr,
                              $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000));
}
// Connect failed?
if ($this->handle_ === FALSE) {
  $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])';
  if ($this->debug_) {
    call_user_func($this->debugHandler_, $error);
  }
  throw new TException($error);
}
$write = array($this->handle_);
$writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_);
if ($writable === 0) {
    die('123');
}

结果显示它打开后立即被阻止!

重新启动php-fpm时,错误会消失一段时间并再次出现。

下面是客户端代码:

$socket = new TSocket('10.0.1.151', 1234, true); 
$framedSocket = new TFramedTransport($socket); 
$transport = $framedSocket;
$protocol = new TCompactProtocol($transport);   
$transport->open();
$client= new userservice'UserServiceProxyClient($protocol);  
$result = $client->findUser($id); 

如果我将 php-fpm 配置pm.max_children从 200 调整为 2,错误也会消失。

我尝试将超时增加到 10 秒,并在 10 秒后超时。

知道问题的原因是什么以及如何解决它吗?

我修改了php节俭库。更改自

$writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_);

$writable = 1;

阅读也是如此。

问题得到解决。

这意味着连接已建立(我什至使用 tcpdump 来确认 tcp 连接已建立)和可写,但选择超时。奇怪的事情!