怀疑workerman/rabbitmq会引起Epoll错误,原因暂不明

cqqjj1029

目前来看Webman程序运行期间一切是正常的,只是当我把服务停止时,会报出很多下面这样的报错:

PHP Warning: Unknown: Epoll MOD(1) on fd 6 failed. Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

webman版本是最新的,workerman版本是最新的,操作系统是centos7.9,php版本7.4.30,event版本也是目前最新的3.0.8

为什么怀疑workerman/rabbitmq

因为当我试着把所有消费监听进程都关掉后,无论怎样开启服务、停止服务、重启服务、重载服务,怎么搞都不会发生Epoll报错。

简单说一下我的操作:

  1. 建一个app/util/Rabbitmq工具类,提供publish和consume方法;
  2. 在控制器里需要发布消息的位置调用Rabbitmq工具类中的publish方法;
  3. 自定义几个consumer进程,编写消费的具体回调函数,并在onWorkerStart中调用Rabbitmq工具类中的消费方法;

代码:

class Rabbitmq
{
    /**
     * 发布消息到worker队列,支持一次性发布多个消息
     *
     * @author Aaron <chenqiang@h024.cn>
     *
     * @param string|array $msgData 需要入队的消息,单一消息为字符串类型,多个消息是数组类型
     * @param string $queueName     队列名称,默认空串就是默认队列
     * @param \PhpAmqpLib\Channel\AMQPChannel $channel  指定rabbit通道,默认null表示使用默认通道
     */
    public static function publishWorkerQueue($msgData, string $queueName = '', \PhpAmqpLib\Channel\AMQPChannel $channel = null)
    {
        // $log = \support\Log::channel('producer');
        if ($queueName == '') {
            $queueName = env('RABBITMQ_DEFAULT_QUEUE', 'default');
        }
        if (is_null($channel)) {
            $channel = \app\bootstrap\Rabbitmq::$defaultProducerChannel;
        }
        $channel->queue_declare($queueName, false, true, false, false);
        if (!is_array($msgData)) {
            $msgData = array($msgData);
        }
        // 遍历数组,对每一个元素做入队操作
        foreach ($msgData as $dataBody) {
            // 把字符串类型的元素入队,忽略其他类型的元素
            try {
                $dataBody = (string)$dataBody;
            } catch (\Exception $e) {
                $dataBody = json_encode($dataBody);
            }
            $msg = new AMQPMessage(
                $dataBody,
                array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT)
            );
            // $log->debug("{$queueName}入队数据: {$dataBody}");
            $channel->basic_publish($msg, '', $queueName);
        }
    }

    /**
     * 用workerman的rabbitmq组件实现的异步消息发布
     *
     * @author Aaron <chenqiang@h024.cn>
     *
     * @param array $option    // 连接客户端的配置
     * @param string $queue                         // 指定队列名
     * @param string $message                       // 要发布的消息
     */
    public static function asyncPublishWorkerQueue(array $option, string $queue, string $message)
    {
        (new \Workerman\RabbitMQ\Client($option))->connect()->then(function (\Workerman\RabbitMQ\Client $client) use ($message) {
            return $client->channel();
        })->then(function (\Bunny\Channel $channel) use ($message, $queue) {
            return $channel->queueDeclare($queue, false, true, false, false)->then(function () use ($channel) {
                return $channel;
            });
        })->then(function (\Bunny\Channel $channel) use ($message, $queue) {
            $briefMsg = mb_substr($message, 0, 50);
            // \support\Log::channel('producer')->debug(" [x] Sending {$briefMsg} to {$queue}\n");
            return $channel->publish($message, [], '', $queue)->then(function () use ($channel) {
                return $channel;
            });
        })->then(function (\Bunny\Channel $channel) use ($message, $queue) {
            $briefMsg = mb_substr($message, 0, 50);
            // \support\Log::channel('producer')->debug(" [x] Sent {$briefMsg} to {$queue}\n");
            $client = $channel->getClient();
            // return $client;
            return $channel->close()->then(function () use ($client) {
                return $client;
            });
        // });
        })->then(function (\Workerman\RabbitMQ\Client $client) {
            $client->disconnect();
        });
    }

    /**
     * 通过workerman的rqbbitmq组件实现的异步队列消费,需要执行$channel->ack方法以完成消费
     *
     * @author Aaron <chenqiang@h024.cn>
     *
     * @param array $option     // 连接客户端的配置
     * @param string $queue
     * @param callable $callback                    // 消费的具体回调方法,接收参数\Bunny\Message $message, \Bunny\Channel $channel, \Workerman\RabbitMQ\Client $client
     */
    public static function asyncConsumeWorkerQueue(array $option, string $queue, callable $callback, bool $noAck = MQ_NO_AUTO_ACK)
    {
        (new \Workerman\RabbitMQ\Client($option))->connect()->then(function (\Workerman\RabbitMQ\Client $client) {
            return $client->channel();
        })->then(function (\Bunny\Channel $channel) use ($queue) {
            return $channel->queueDeclare(
                $queue,
                false,
                true,
                false,
                false
            )->then(function () use ($channel) {
                return $channel;
            });
        })->then(function (\Bunny\Channel $channel) {
            return $channel->qos(0, 1, false)->then(function () use ($channel) {
                return $channel;
            });
        })->then(function (\Bunny\Channel $channel) use ($callback, $queue, $noAck) {
            echo " [*] Waiting for messages on {$queue}.\n";
            // noAck = false表示需要消费进程手动判断
            $channel->consume(
                $callback,
                $queue,
                '',
                false,
                $noAck
            );
        });
    }
}
class TestConsumer
{
    public function onWorkerStart(Worker $worker)
    {
        // 回调函数就是具体的队列消费者程序
        $callback = array($this, 'asyncConsume');
        // 用异步rabbitmq组件来监听队列并完成消费,不会造成worker阻塞
        \util\Rabbitmq::asyncConsumeWorkerQueue(
            \app\bootstrap\Rabbitmq::$asyncConsumerOption,
            QUEUENAME_TEST,
            $callback
        );
    }

    /**
     * 异步消费回调函数
     *
     * @author Aaron <chenqiang@h024.cn>
     *
     * @param \Bunny\Message $message               异步消息体
     * @param \Bunny\Channel $channel               异步队列通道
     * @param \Workerman\RabbitMQ\Client $client    异步队列客户端
     */
    public function asyncConsume(Message $message, Channel $channel, Client $client)
    {
        \support\Log::channel('consumer')->debug("test消费者: 收到消息 " . $message->content);
        $data = json_decode($message->content, true);
        /// ... 具体消费动作 ...
        $channel->ack($message);
        \support\Log::channel('consumer')->debug("test消费者: 消费完毕 " . $message->content);
    }
}

报错过程:

Epoll数量与rabbitmq队列消费进程数量正相关

# php start.php start
Workerman[start.php] start in DEBUG mode
-------------------------------------------------- WORKERMAN --------------------------------------------------
Workerman version:4.0.33          PHP version:7.4.30
--------------------------------------------------- WORKERS ---------------------------------------------------
proto   user            worker                       listen                      processes    status           
tcp     root            data_worker                  http://0.0.0.0:9607         8             [OK]            
tcp     root            esl_listener                 none                        1             [OK]            
tcp     root            async_task_proxy             websocket://0.0.0.0:9608    1             [OK]            
tcp     root            task_init                    text://0.0.0.0:9611         4             [OK]            
tcp     root            task_start                   text://0.0.0.0:9612         4             [OK]            
tcp     root            task_call_consumer           none                        4             [OK]            
tcp     root            task_data_update_consumer    none                        4             [OK]            
tcp     root            task_billing_consumer        none                        8             [OK]            
---------------------------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.
 [*] Waiting for messages on task_data_update.
 [*] Waiting for messages on task_data_update.
 [*] Waiting for messages on task_call.
 [*] Waiting for messages on task_data_update.
 [*] Waiting for messages on task_billing.
 [*] Waiting for messages on task_billing.
 [*] Waiting for messages on task_billing.
 [*] Waiting for messages on task_data_update.
 [*] Waiting for messages on task_billing.
 [*] Waiting for messages on task_billing.
 [*] Waiting for messages on task_billing.
 [*] Waiting for messages on task_call.
 [*] Waiting for messages on task_billing.
 [*] Waiting for messages on task_billing.
 [*] Waiting for messages on task_call.
 [*] Waiting for messages on task_call.
^CPHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
Workerman[start.php] stopping ...
PHP Warning:  Unknown: Epoll MOD(1) on fd 8 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 8 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 8 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 8 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 8 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 8 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 8 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 8 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 8 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 8 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 8 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 8 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 8 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 8 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 8 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 8 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(1) on fd 6 failed.  Old events were 6; read change was 0 (none); write change was 2 (del): Bad file descriptor in Unknown on line 0
PHP Warning:  Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0

Warning: Unknown: Epoll MOD(4) on fd 6 failed.  Old events were 6; read change was 2 (del); write change was 0 (none): Bad file descriptor in Unknown on line 0
Workerman[start.php] has been stopped
855 1 0
1个回答

chaz6chez

rabbitmq client用单例,一个进程保持一个连接,你的epoll在fd 6上面失败了,失败原因就是读取不到描述文件了,一般情况就是你的loop覆盖了;
每个rabbitmq client会复用当前进程的globalEvent,如果使用new的话,每次的client对象都不是同一个;
除了上述问题的可能性外,还有一种:当你的消费进程启动后,client的消费是异步的,也就是你在这一个loop内进行stop的话,你的消费可能还正在消费中,相当于在循环中中止该消费loop,可能因为一些意外情况不能等待消费完毕后中止,而是直接中断;
当然具体的还是得具体代码来分析;

推荐webman环境下可以直接使用rabbitmq客户端插件,是生产可用的,可以放心使用;
https://www.workerman.net/plugin/67

  • 暂无评论
年代过于久远,无法发表回答
🔝