2017-12-27 960 views
0

我试图在我的项目中使用Server Side Events机制。 (这类似于类固醇上的长轮询)Apache进程不会在与RabbitMQ断开连接后死亡

来自“Sending events from the server”字幕的例子效果非常好。几秒钟后,断开连接,apache进程终止。此方法工作正常。

但是!如果我尝试使用RabbitMQ,则在浏览器从服务器断开连接后,Apache不会导致进程中断(es.close())。并且进程保持原样并在docker容器重新启动后才会被杀死。

connection_abortedconnection_status根本不起作用。 connection_aborted仅返回0,即使断开连接,connection_status也返回CONNECTION_NORMAL。只有当我使用RabbitMQ时才会发生。没有RMQ这个功能运作良好。

ignore_user_abort(false)也不起作用。

代码示例:

<?php 
use PhpAmqpLib\Channel\AMQPChannel; 
use PhpAmqpLib\Connection\AbstractConnection; 
use PhpAmqpLib\Exception\AMQPTimeoutException; 
use PhpAmqpLib\Message\AMQPMessage; 

class RequestsRabbit 
{ 
    protected $rabbit; 

    /** @var AMQPChannel */ 
    protected $channel; 

    public $exchange = 'requests.events'; 

    public function __construct(AbstractConnection $rabbit) 
    { 
     $this->rabbit = $rabbit; 
    } 

    public function getChannel() 
    { 
     if ($this->channel === null) { 
      $channel = $this->rabbit->channel(); 

      $channel->exchange_declare($this->exchange, 'fanout', false, false, false); 

      $this->channel = $channel; 
     } 

     return $this->channel; 
    } 

    public function send($message) 
    { 
     $channel = $this->getChannel(); 

     $message = json_encode($message); 

     $channel->basic_publish(new AMQPMessage($message), $this->exchange); 
    } 

    public function subscribe(callable $callable) 
    { 
     $channel = $this->getChannel(); 

     list($queue_name) = $channel->queue_declare('', false, false, true, false); 

     $channel->queue_bind($queue_name, $this->exchange); 

     $callback = function (AMQPMessage $msg) use ($callable) { 
      call_user_func($callable, json_decode($msg->body)); 
     }; 

     $channel->basic_consume($queue_name, '', false, true, false, false, $callback); 

     while (count($channel->callbacks)) { 
      if (connection_aborted()) { 
       break; 
      } 

      try { 
       $channel->wait(null, true, 5); 
      } catch (AMQPTimeoutException $exception) { 
      } 
     } 

     $channel->close(); 
     $this->rabbit->close(); 
    } 
} 

会发生什么:

  • 浏览器建立连接SSE到服务器。 var es = new EventSource(url);
  • Apache2产生新的进程来处理这个请求。
  • PHP生成一个新的队列并连接到它。
  • 浏览器关闭连接es.close()
  • Apache2不会终止进程并保持原样。 RabbitMQ队列不会被删除。如果我做了一些重新连接,它会产生一堆进程和一堆队列(1重新连接= 1进程= 1队列)。
  • 我关闭所有选项卡 - 活动进程。我关闭浏览器 - 相同的情况。

寻找某种类型的PHP错误。还是Apach2?

我用什么:

一些截图:

RabbitMQ queues

Processes

请帮我弄清楚是怎么回事...

附:对不起我的英语不好。如果您可以找到错误或错字,请在评论中指出它。我会很感激:)

回答

0

,如果你在你的服务器端事件使用send()subscribe()(或两者)你不说。假设你正在使用subscribe()没有错误。这个循环:

while (count($channel->callbacks)) { 
    if (connection_aborted()) { 
     break; 
    } 

    try { 
     $channel->wait(null, true, 5); 
    } catch (AMQPTimeoutException $exception) { 
    } 
} 

将运行,直到进程被杀死或连接远离的RabbitMQ关闭。收听排队消息时这是正常的。如果你需要在某个时候停止循环,你可以设置一个变量来检查循环,或者在SSE结束时抛出异常(尽管我觉得这很尴尬)。

+0

编号进程不会被apache2杀死。这是自2004年以来最古老的错误 - https://bugs.php.net/bug.php?id=30301。 'connection_aborted'和'connection_status'函数不起作用。而这些无法解决它。对我来说,最好使用Websocketd(最后用D) –