【问题标题】:Apache process doesn't die after disconnection with RabbitMQ与 RabbitMQ 断开连接后,Apache 进程不会死机
【发布时间】:2017-12-27 17:28:46
【问题描述】:

我试图在我的项目中使用Server Side Events 机制。 (这就像类固醇上的长轮询)

来自“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 错误。还是 Apache2 的?

我用什么:

一些截图:

请帮我弄清楚发生了什么...

附:对不起我的英语不好。如果您能找到错误或拼写错误,请在 cmets 中指出。我将不胜感激:)

【问题讨论】:

    标签: php apache docker rabbitmq amqp


    【解决方案1】:

    您不会说在服务器端事件期间您使用的是send() 还是subscribe()(或两者)。假设您使用 subscribe() 没有错误。这个循环:

    while (count($channel->callbacks)) {
        if (connection_aborted()) {
            break;
        }
    
        try {
            $channel->wait(null, true, 5);
        } catch (AMQPTimeoutException $exception) {
        }
    }
    

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

    【讨论】:

    • 没有。进程不会被 apache2 杀死。这是自 2004 年以来最古老的错误 -- bugs.php.net/bug.php?id=30301connection_abortedconnection_status 函数不起作用。而这些没有办法解决。对我来说最好使用 Websocketd(最后是 D)
    猜你喜欢
    • 2014-07-30
    • 2012-06-17
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    相关资源
    最近更新 更多