跳出 Gearman 循环

Breaking out of a Gearman loop

我有一个 php 应用程序可以从我们的服务器获取部件号请求。届时,我们会联系第三方 API 以收集定价信息,以确保我们拥有该特定请求的最新定价。有时第三方 API 速度很慢或可能已关闭,因此我们有一个数据库存储每个特定零件号的最新定价请求,我们可以将其用作后备。我想 运行 向第三方 API 请求并使用 Gearman 并行处理数据库。这是想法:

  1. 接收请求
  2. 通过gearman,创建两个job:
    • 请求第三方API
    • MySQL 数据库查找
  3. 循环等待,return结果基于以下条件:
    • 如果第三方API完成了return那个结果,return那个结果立即
    • 如果经过一段时间(例如 2 秒)并且第三方 API 没有响应,return MySQL 查找数据

使用 gearman,我的想法是 运行 前台的两个任务并在 setCompleteCallback() 调用中突破 运行Tasks(),或者 运行它们在后台并在单独的循环中检查这两个任务,并使用 jobStatus() 检查任务。

不幸的是,我无法在仍然可以访问结果数据的同时为我工作。有没有更好的方法,或者是否有一些现有的例子说明有人是如何做到这一点的?

我认为您已经描述了一个单一的阻塞问题,即第 3 方 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' 的作业,并将包含产品 ID 5 的工作负载数据传递给它。它将等待结果返回,并检查 success 属性。如果它存在并且为真,则 api 查找成功。

想法是在工作任务中设置超时条件,这完全取决于您如何实现 API 查找。如果您使用的是 cURL(或 cURL 的一些包装器),您可以查看如何检测超时的答案 here.

从工人方面:

$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 when it has decided to timeout