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

时间:2012-03-08 12:32:37

标签: php gearman

我遇到的问题是,在PHP应用程序中,Gearman作业有时会传递给多个工作者。我可以减少代码将其重现为一个文件。现在我不确定这是Gearman中的错误还是pecl库中的错误,也可能是我的代码中的错误。

以下是重现错误的代码:

#!/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();
    }
}

当您在命令行上运行此代码时,它应输出 "No job has been passed to more than one worker." ,但它总是输出一些已传递给多个工作程序的作业列表。如果您设置$sWorker = 'standard';'exception-sleep',则不会显示错误。

如果您可以运行代码并告诉我您是否能够重现错误,如果我在代码中有错误,那将对我有所帮助。

1 个答案:

答案 0 :(得分:3)

与Gearman 0.24,PECL lib 1.0.2完全相同的问题。能够每次都使用您的脚本重现错误。

旧版本的Gearman(我认为0.14)以前工作正常。

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