Gearman作业传递给多个工人(PHP)

Gearman作业传递给多个工人(PHP),php,gearman,Php,Gearman,我的问题是,在PHP应用程序中,Gearman作业有时会传递给多个工人。我可以减少代码,将其复制到一个文件中。现在我不确定这是Gearman中的bug还是pecl库中的bug,或者是我的代码中的bug 以下是重现错误的代码: #!/usr/bin/php <?php // Try 'standard', 'exception' or 'exception-sleep'. $sWorker = 'exception'; // Detect run mode "client" or "

我的问题是,在PHP应用程序中,Gearman作业有时会传递给多个工人。我可以减少代码,将其复制到一个文件中。现在我不确定这是Gearman中的bug还是pecl库中的bug,或者是我的代码中的bug

以下是重现错误的代码:

#!/usr/bin/php
<?php

// Try 'standard', 'exception' or 'exception-sleep'.
$sWorker = 'exception';



// Detect run mode "client" or "worker".
if (!isset($argv[1]))
    $sMode = 'client';
else
    $sMode = 'worker-' . $sWorker;

$sLogFilePath = __DIR__ . '/log.txt';

switch ($sMode) {

    case 'client':

        // Remove all queued test jobs and quit if there are test workers running.
        prepare();

        // Init the greaman client.
        $Client= new GearmanClient;
        $Client->addServer();

        // Empty the log file.
        file_put_contents($sLogFilePath, '');

        // Start some worker processes.
        $aPids = array();       
        for ($i = 0; $i < 100; $i++)
            $aPids[] = exec('php ' . __FILE__ . ' worker > /dev/null 2>&1 & echo $!');

        // Start some jobs. Also try doHigh(), doBackground() and
        // doBackgroundHigh();
        for ($i = 0; $i < 50; $i++)
            $Client->doNormal('test', $i);

        // Wait a second (when running jobs in background).
        // sleep(1);

        // Prepare the log file entries.
        $aJobs = array();
        $aLines = file($sLogFilePath);
        foreach ($aLines as $sLine) {
            list($sTime, $sPid, $sHandle, $sWorkload) = $aAttributes = explode("\t", $sLine);
            $sWorkload = trim($sWorkload);
            if (!isset($aJobs[$sWorkload]))
                $aJobs[$sWorkload] = array();
            $aJobs[$sWorkload][] = $aAttributes;
        }

        // Remove all jobs that have been passed to only one worker as expected.
        foreach ($aJobs as $sWorkload => $aJob) {
            if (count($aJob) === 1)
                unset($aJobs[$sWorkload]);
        }

        echo "\n\n";

        if (empty($aJobs))
            echo "No job has been passed to more than one worker.";
        else {
            echo "Those jobs has been passed more than one times to a worker:\n";
            foreach ($aJobs as $sWorload => $aJob) {

                echo "\nJob #" . $sWorload . ":\n";
                foreach ($aJob as $aAttributes)
                    echo "  $aAttributes[2] (Worker PID: $aAttributes[1])\n";
            }
        }

        echo "\n\n";

        // Kill all started workers.
        foreach ($aPids as $sPid)
            exec('kill ' . $sPid . ' > /dev/null 2>&1');

    break;

    case 'worker-standard':
        $Worker = new GearmanWorker;
        $Worker->addServer();
        $Worker->addFunction('test', 'logJob');
                    $bShutdown = false;
        while ($Worker->work())
            if ($bShutdown)
                continue;
        break;

    case 'worker-exception':
        try {
            $Worker = new GearmanWorker;
            $Worker->addServer();
            $Worker->addFunction('test', 'logJob');
            $bShutdown = false;
            while ($Worker->work())
                if ($bShutdown)
                    throw new \Exception;

        } catch (\Exception $E) {
        }
    break;

    case 'worker-exception-sleep':
        try {
            $Worker = new GearmanWorker;
            $Worker->addServer();
            $Worker->addFunction('test', 'logJob');
            $bShutdown = false;
            while ($Worker->work())
            {
                if ($bShutdown) {
                    sleep(1);
                    throw new \Exception;
                }
            }
        } catch (\Exception $E) {
        }
    break;
}

function logJob(\GearmanJob $Job)
{
    global $bShutdown, $sLogFilePath;
    $sLine = microtime(true) . "\t" . getmypid() . "\t" . $Job->handle() . "\t" . $Job->workload() . "\n";
    file_put_contents($sLogFilePath, $sLine, FILE_APPEND);
    $bShutdown = true;
}


function prepare()
{
    $rGearman = fsockopen('127.0.0.1', '4730', $iErrno, $sErrstr, 3);
    $aBuffer = array();
    fputs ($rGearman, 'STATUS' . PHP_EOL);
    stream_set_timeout($rGearman, 1);
    while (!feof($rGearman))
        if ('.' . PHP_EOL !== $sLine = fgets($rGearman, 128))
            $aBuffer[] = $sLine;
        else
            break;
    fclose($rGearman);

    $bJobsInQueue = false;
    $bWorkersRunning = false;
    foreach ($aBuffer as $sFunctionLine) {
        list($sFunctionName, $iQueuedJobs, $iRunningJobs, $iWorkers) = explode("\t", $sFunctionLine);
        if ('test' === $sFunctionName) {
            if (0 != $iQueuedJobs)
                $bJobsInQueue = true;
            if (0 != $iWorkers)
                $bWorkersRunning = true;;
        }
    }

    // Exit if there are workers running.
    if ($bWorkersRunning)
        die("There are some Gearman workers running that have registered a 'test' function. Please stop these workers and run again.\n\n");

    // If there are test jobs in the queue start a worker that eat up the jobs.
    if ($bJobsInQueue) {
        $sPid = exec('gearman -n -w -f test > /dev/null 2>&1 & echo $!');
        sleep(1);
        exec ("kill $sPid > /dev/null 2>&1");
        // Repeat this method to make sure all jobs are removed.
        prepare();
    }
}
#/usr/bin/php

Gearman 0.24和PECL lib 1.0.2存在完全相同的问题。每次都能用脚本重现错误

Gearman的一个旧版本(我认为是0.14)曾经工作得很好


将Gearman升级到0.33解决了这个问题。

当我运行代码时,我得到了预期的输出。您使用的是哪个Gearman版本?谢谢您的测试。我已经在0.28和0.27上测试了它,并在Linux上使用了PECL lib版本1.0.1和1.0.2。您使用的是什么版本和操作系统?我运行的是0.29,但我只是再试了一次,并且能够重现错误(这只会偶尔发生,因为在第一次测试中,我至少有3-4次没有收到错误)。0.24、PECL lib 1.0.2也有同样的问题