phprabbitMq消耗守护程序失败,并出现mysql管道损坏错误

时间:2020-01-04 05:31:20

标签: php mysql rabbitmq propel

我目前正在尝试还原旧应用程序的功能,以及部分无法使用(消耗部分MQ消息)逻辑的逻辑部分(诚实的是,我什至不确定它之前是否能正常工作)。这个程序有自定义的逻辑来运行处理兔子消息的工作者。问题是,当工作人员在运行时总是对数据库执行任何操作(获取一些数据,关闭连接等)失败,而错误为send of 291 bytes failed with errno=32 Broken pipe。使用Propel ORM进行应用。首先,我将分享propel配置文件:

config.propel.php:

<?php
use Propel\Runtime\Propel;
use Propel\Runtime\Connection\ConnectionManagerSingle;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$loggerConfig = $config->get('logger')['sql'];
$db = $config->get('db');
$env = $config->get('app')['env'];

$defaultLogger = new Logger('defaultLogger');
$defaultLogger->pushHandler(new StreamHandler($loggerConfig['filename'], $loggerConfig['level']));
Propel::getServiceContainer()->setLogger('defaultLogger', $defaultLogger);

$queryLogger = new Logger('main');
$queryLogger->pushHandler(new StreamHandler($loggerConfig['filename'], $loggerConfig['level']));
Propel::getServiceContainer()->setLogger('main', $queryLogger);

$serviceContainer = Propel::getServiceContainer();
$serviceContainer->setAdapterClass('main', 'mysql');

$manager = new ConnectionManagerSingle();

$dsn = 'mysql:host='.$db['host'].';';

if (isset($db['port'])) {
    $dsn .= 'port=' . $db['port'] . ";";
} else {
    $dsn .= 'port=3306;';
}

if (isset($db['charset'])) {
    $dsn .= 'charset='.$db['charset'].";";
}

$dsn .= 'dbname='.$db['dbname'];

$manager->setConfiguration([
    'dsn' => $dsn,
    'user'     => $db['user'],
    'password' => $db['password'],
]);

$serviceContainer->setConnectionManager('main', $manager);

if ($env == 'development') {
    $serviceContainer->getWriteConnection('main')->useDebug(true);
}

bootstrap.daemon.php实际上用于引导该工作人员的应用程序:

    <?php

    require 'lib/vendor/autoload.php';
    require 'app/autoload.php';
    // app config is just set of configurations like database credentials etc. 
    require 'app/config.php';
  // provided above
    require_once 'app/config.propel.php';

start脚本本身(启动工作程序的守护程序):

#!/usr/bin/php -q
<?php
set_time_limit(0);

require __DIR__.'/../../bootstrap.daemon.php';
// it's a routing (not sure why it here)
require __DIR__.'/../../app/config.public.php';

use App;
use Luncher;

App::start($config);

/*
    Here is some magic:)
    Do not change
 */
{
    ini_set('mysql.connect_timeout', 0);
    ini_set('default_socket_timeout', 0);
}

$l = new Luncher;
$l->startDeamon();

Launcher

<?php
namespace App\Daemons\Core;

use App\App;
use App\Daemons\Core\ServiceWrapper;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

class Luncher
{
    private static $daemonsNamespace = "App\Daemons\\";

    public function __construct()
    {
        set_error_handler(function($errno, $errstr, $errfile, $errline) {
            $this->out("[ERROR] ". $errstr." at line ".$errline." in file ".$errfile);
            die();
        });
    }


    public function startDeamon($daemonName = 'Deamon)
    {
        $className = self::$daemonsNamespace.$daemonName;
        if (class_exists($className)) {
            $class = new \ReflectionClass($className);

            $logger = new Logger('daemon');
            $logger->pushHandler(new StreamHandler($daemonConfig['log']));

            $serives = new ServiceWrapper($logger);

            // start 5 workers  
            for ($i=0; $i<5; $i++) {
                echo date("d.m.Y H:i")." Starting daemon $daemonName\n";
                $className::start($serives);
            }
        } else {
            echo date("d.m.Y H:i")." Class $className not found!\n";
        }
    }

    private function stopDaemon($daemonName = 'Daemon')
    {
        $daemonPid = $this->getDaemonPids(daemonName);
        if (strlen($daemonPid)) {
            $this->out("Killing daemon $daemonName");
            exec("kill ".$daemonPid);
        }
    }

    private function getDaemonPids($daemonName)
    {
        $result = [];
        $tmp = [];
        exec("pidof daemons.$daemonName", $tmp);

        if (isset($tmp[0])) {
            $result = explode(' ', $tmp[0]);
        }

        return $result;
    }

    private function out($str)
    {
         echo date("d.m.Y H:i")." $str \n";
    }
}

Deamon本身:

<?php
namespace App\Daemons\Core\Queue;

use App\App;
use Propel\Runtime\Propel;
use App\Daemons\Core\ServiceWrapper;

class Daemon
{
    protected $isParent = true;
    protected $AMQPChannel;
    protected $services;
    protected $currentMsg;
    protected $reinitializeAttempts = 0;
    const MAX_REINITIALIZE_ATTEMPTS = 2;

    public static function start(ServiceWrapper $services)
    {
        $d = new static;
        $d->services = $services;
        $d->fork();

        if ($d->isChild()) {
            $d->services->setPid(getmypid());
            $d->run();
        }
    }


    protected function fork()
    {
        $this->isParent = pcntl_fork();
    }

    protected function isChild()
    {
        if (!$this->isParent) {
            return true;
        } else {
            return false;
        }
    }

    final protected function run()
    {
        try {
            $fErrorHandler = function($errno, $errstr, $errfile, $errline) {
                $this->error('[ERROR] '.$errstr." at line ".$errline." in file ".$errfile);
                die();
            };
            set_error_handler(function($errno, $errstr, $errfile, $errline) use ($fErrorHandler) {
                $fErrorHandler($errno, $errstr, $errfile, $errline);
            });

            register_shutdown_function(function() use ($fErrorHandler) {
                $error = error_get_last();

                if ($error !== null) {
                    $errno   = $error["type"];
                    $errfile = $error["file"];
                    $errline = $error["line"];
                    $errstr  = $error["message"];
                    $fErrorHandler($errno, $errstr, $errfile, $errline);
                }
            });


            $this->_init();

            $callback = function($msg) {
                if (!is_null($msg) && is_array($msg)) {
                    $this->output("Got a new message from queue");
                    $this->output(json_encode($msg));
                    $this->currentMsg = $msg;
                    $this->onNewMessage($msg);
                    $this->currentMsg = null;
                    $this->reinitializeAttempts = 0;
                }
            };

            if ($this->currentMsg) {
                $callback($this->currentMsg);
            }

            $this->consume('long_jobs', $callback);
            $this->runHandling();
        } catch (\Exception $e) {
            $this->error('[EXCEPTION] '.$e->getMessage()." ".$e->getFile()." : ". $e->getLine());

            /**
             * we can't get error code for PDOException :(
             */
            if ($e instanceof \PDOException || $e instanceof \Doctrine\DBAL\DBALException) {
                if ($this->reinitializeAttempts < self::MAX_REINITIALIZE_ATTEMPTS) {
                    $this->output('Trying to reinitialize daemon');
                    $this->reinitializeAttempts++;
                    $this->run();
                    return;
                } else {
                    $this->error('Max reinitialize attempts were achivied');
                }
            }
            die();
        }
    }

    protected function output($msg)
    {
        $this->services->output($msg);
    }

    protected function error($msg)
    {
        $this->services->error($msg);
    }

    protected function onNewMessage($data)
    {
        $this->output("Got a new job");

        $job = new LongJob();


        $this->output("Lunching job ".$data['jobName']);

        $job->setServices($this->services);

        $t1 = microtime(true);
        $job->run($data['data']);
        Propel::closeConnections();
        $this->output("Job is finished. time: ".(microtime(true) - $t1)." seconds");
    }

    private function _init()
    {
        $config = App::getConfig();

        $connection= new AMQPConnection($config->host, $config->port, $config->user, $config->password);
        $this->AMQPChannel = $connection->channel();
    }

    public function consume($queueName, $userCallback)
    {
        $callback = function($msg) use ($userCallback) {
            $userCallback(json_decode($msg->body, true));
            $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
        };
        $this->AMQPChannel->basic_qos(null, 1, null);
        $this->AMQPChannel->queue_declare($queueName, false, true, false, false);
        $this->AMQPChannel->basic_consume($queueName, '', false, false, false, false, $callback);
    }

    public function runHandling()
    {
        while (count($this->AMQPChannel->callbacks)) {
            $this->channel->wait();
        }

        $this->AMQPChannel->close();
    }
}

还有LongJob

namespace App\Daemons\LongJobs;

use App\Daemons\Core\LongJob;
use App\Models\User;
use App\App;
use App\EventsServer\Core\Event;
use App\EventsServer\Core\EventsManager;
use App\Models\AR\Payments\Payments;
    class LongJob {
        protected $services;
        public function setServices(IServices $services)
        {
            $this->services = $services;
        }

        public function run($data)
        {
            if (!isset($data['userId'])) {
                $this->services->error("There is no user id!");
                return;
            }

            $compatProductId = ProductQuery::create()
                ->filterBySku(Product::SKU_COMPAT)
                ->findOne()
                ->getId();


            $oUser = User::find($data['userId']);

            if (!$oUser) {
                $this->services->error("Can't find user with id ". $data['userId']);
                return;
            }

            $arPurchase = PurchaseQuery::create()
                ->filterByProductId($compatProductId)
                ->find();

            foreach ($arPurchase as $purchase) {
                if ($data['userId'] != $purchase->getUserId()) {
                    $oPurchasedUser = User::find($purchase->getUserId());
                    $oPayments = new Payments($oPurchasedUser);
                    $oPayments->calcAndSaveCompat([$oUser]);
                }
            }
        }

LongJob正在运行并且首次使用数据库时,它将引发该错误。很抱歉,没有代码,但是我不知道该如何共享该问题。老实说,几年前代码还不是很整洁。根据我的经验,我总是将Rabbit与一些现代框架一起使用,因此我对自己创建这样的守护进程并不十分熟悉,而且我也不知道如何解决它:(

0 个答案:

没有答案