2017-12-27 14 views
0

私のプロジェクトでメカニックServer Side Eventsを使用しようとしていました。 (これはステロイドのロングポーリングのようなものです)RabbitMQとの接続解除後にApacheプロセスが終了しない

Sending events from the server」の例題は、美しく動作します。数秒後に切断されると、Apacheプロセスが強制終了されます。このメソッドは正常に動作します。

BUT! RabbitMQを使用しようとすると、ブラウザからサーバーとの接続が切断された後にプロセスが終了しない(es.close())。処理はそのままで、ドッカーコンテナが再起動した後でのみ終了します。

connection_abortedおよびconnection_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

してください、私は何が起こっているかを把握するのに役立つ...

P.S.私の英語には申し訳ありません。間違いやタイプミスが見つかった場合は、そのコメントをポイントしてください。私は非常に感謝します:)

答えて

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が付いている)を使う方が良いです –

関連する問題