Выход из петли Gearman

У меня есть приложение php, которое получает запросы на номера деталей с нашего сервера. В этот момент мы обращаемся к стороннему API для сбора информации о ценах, чтобы убедиться, что у нас есть последние цены для этого конкретного запроса. Иногда сторонний API работает медленно или может быть недоступен, поэтому у нас есть база данных, в которой хранятся последние запросы цен для каждого конкретного номера детали, которые мы можем использовать в качестве запасного варианта. Я хотел бы запустить запрос к стороннему API и базе данных параллельно с помощью Gearman. Вот идея:

  1. Получить запрос
  2. Through gearman, create two jobs:
    • Request to third party API
    • Поиск в базе данных MySQL
  3. Wait in a loop and return the results based on the following conditions:
    • If the third party API has completed return that result, return that result immediately
    • Если истекло время (например, 2 секунды), а сторонний API не ответил, верните данные поиска MySQL.

Используя gearman, я думал либо запустить две задачи на переднем плане и выйти из runTasks() в вызове setCompleteCallback(), либо запустить их в фоновом режиме и проверить две задачи в отдельном цикле и проверить в задачах с помощью jobStatus().

К сожалению, я не могу заставить ни один из маршрутов работать на меня, сохраняя при этом доступ к результирующим данным. Есть ли лучший способ или есть какие-то существующие примеры того, как кто-то сделал эту работу?


person jloosli    schedule 27.03.2015    source источник
comment
Я думаю, что единственная задача Gearman, которая вам понадобится, — это поиск API, поиск MySQL, который вы можете выполнить из клиентского приложения, верно?   -  person Jeff Lambert    schedule 27.03.2015
comment
Единственная причина, по которой я выполняю поиск SQL, - это резервная копия на случай, если сторонний API запаздывает/неисправен. Это не обязательно должна быть отдельная задача, но я думаю, что, по крайней мере, я могу сразу же получить результаты, если у меня не хватит времени. В любом случае, у меня все еще нет возможности запускать сторонний поиск в фоновом режиме и ждать, пока не закончится мой лимит, прежде чем двигаться дальше.   -  person jloosli    schedule 27.03.2015


Ответы (1)


Я думаю, вы описали единственную проблему блокировки, а именно результаты поиска стороннего API. С моей точки зрения, у вас есть два способа справиться с этим: либо вы можете полностью прервать попытку, если решите, что у вас закончилось время, либо вы можете сообщить клиенту, что у вас закончилось время, но продолжить работу с в любом случае, просто для обновления вашего локального кеша на тот случай, если он будет отвечать медленнее, чем вам хотелось бы. Я опишу, как бы я решил первую проблему, потому что это было бы проще.

Со стороны клиента:

$request = array(
    'productId' => 5,
);
$client = new GearmanClient( );
$client->addServer( '127.0.0.1', 4730 );
$results = json_decode($client->doNormal('apiPriceLookup', json_encode( $request )));

if($results && property_exists($results->success) && $results->success) {
    // Use local data
} else {
    // Use fresh data
}

Это создаст задание на сервере заданий с именем функции 'apiPriceLookup' и передаст ему данные рабочей нагрузки, содержащие идентификатор продукта 5. Он дождется возврата результатов и проверит свойство success. Если он существует и истинен, то поиск API прошел успешно.

Идея состоит в том, чтобы установить условие тайм-аута в рабочей задаче, что полностью зависит от того, как вы реализуете поиск API. Если вы используете cURL (или какую-либо оболочку для cURL), вы можете увидеть ответ на вопрос, как определить тайм-аут здесь.

С рабочей стороны:

$worker= new GearmanWorker(); 
$worker->addServer(); 
$worker->addFunction("apiPriceLookup", "apiPriceLookup", $count);

while ($worker->work());

function apiPriceLookup($job) {
    $payload = json_decode($job->workload());

    try {
        $results = [
            'data' => PerformApiLookupForProductId($payload->productId),
            'success' => true,
        ];
    } catch(Exception $e) {
        $results = ['success' => false];
    }

    return json_encode($results);
}

Это просто создает объект GearmanWorker и подписывает его на функцию apiPriceLookup. Он будет вызывать функцию apiPriceLookup всякий раз, когда клиент отправляет задачу на сервер заданий. Эта функция вызывает другую функцию, PerformApiLookupForProductId, которая должна быть написана так, чтобы генерировать исключение всякий раз, когда возникает условие тайм-аута.

Я не думаю, что это будет рассматриваться как использование исключений для управления логическим потоком, я думаю, что условия тайм-аута обычно являются исключительными (или должны быть) событиями. Например, Guzzle выдаст GuzzleHttp\Exception\RequestException , когда решит тайм-аут.

person Jeff Lambert    schedule 27.03.2015
comment
Я согласен, что это был бы хороший способ, но, к сожалению, я не могу просто выдать исключение, так как я хочу, чтобы процесс продолжался и записывал данные в базу данных, чтобы они были там в следующий раз, когда я делаю запрос. - person jloosli; 28.03.2015
comment
Для этого у вас есть несколько вариантов, пара, которая приходит на ум, настраивает cron, чтобы просто вызывать вашу задачу для обновления из вашего API всех продуктов каждый час (или день, или два раза в день), а затем полагаться только на ваш локальный кешировать и смириться с тем, что у вас есть данные дневной давности. Или вы можете запустить фоновый процесс, который указывает статус 1/2 (числитель/знаменатель), когда возникает условие тайм-аута, и 2/2, когда результаты были успешно получены, а затем заблокировать на веб-сервере, пока вы ждете, пока статус не изменится. быть обновленным - person Jeff Lambert; 28.03.2015
comment
Собираюсь принять это как лучший ответ. Однако, в конце концов, я решил запустить это в фоновом режиме, и фоновая задача отправила ответ в хранилище Redis, чтобы мое основное приложение могло проверить там, есть ли какие-либо данные. Кажется, работает нормально. - person jloosli; 30.03.2015