繁体   English   中英

一个函数如何在php中同时运行多次?

[英]How can a function be run multiple times at the same time in php?

我有一个用 cron 运行的页面。 此页面上的函数向链接发送请求,该请求返回的示例 json 如下所示。

First Request

 {
  "next": "randomToken123",
  "previous": null,
  "items": [
    {
     ....
    },
    {
     ....
    }
   ]
 }

Second Request

     {
      "next": "randomToken456",
      "previous": null,
      "items": [
        {
         ....
        },
        {
         ....
        }
       ]
     }

我在这里要做的主要事情是能够发送另一个请求,而无需等待在前一个请求之后采取的操作,方法是在第一个请求之后返回下一个值。 因此,让我通过一个函数示例向您展示:

public function cronFunc($next_cursor = null) {
    $result = doRequest($next_cursor);
    updateTheDatabaseTable($result);
}

我想通过同时运行上述函数3次同时将返回的项目添加或更新到数据库中。 所以,我想同时执行 3 个事务。 是否可以用线程来做到这一点? 还是有其他方法? 我尝试使用 header('Location:...') 函数来执行此操作,但过了一会儿它给出了一个错误,说 too_many_redirects。

将有大约 100 万+个数据,每个请求将进行 200 次更新。 因此,由于我想在这里运行 3 次,我将一次进行 600 次更新。

我提供了两种解决方案来解决这个问题。 一种是使用多线程,一种是使用 cronjobs。

在 PHP 中使用线程的解决方案

PHP中有一些支持多线程的库。

安装 PCNTL 和alfallouji/php_multithread

您可以使用以下命令安装此多线程库:

composer require alfallouji/php_multithread

这取决于带有以下软件包的 PCNTL:

sudo apt-get install php-cli

概念

  • (1) 无令牌运行第一个请求
  • (1) 使用下一个令牌获取响应
  • (1) 使用新令牌启动新线程
    • (2) 使用新令牌运行第二个请求
    • (2) 使用下一个令牌获取响应
    • (2) 使用新令牌启动新线程
    • ...第三个请求等...
    • (2) 处理第二个请求
  • (1) 处理第一个请求

使用alfallouji/php_multithread

此代码是对上述概念的模拟。 将以下代码添加到 test.php,然后运行php test.php

<?php

require_once __DIR__ . "/vendor/autoload.php";

$manager = new Threading\Multiple(100);

class Task extends \Threading\Task\Base
{
    private $params = [], $manager;

    public function __construct(array $params, \Threading\Multiple $manager)
    {
        $this->params = $params;
        $this->manager = $manager;
    }

    public function initialize() 
    {
        return true;
    }

    public function onSuccess()
    {
        return true;
    }

    public function onFailure() 
    {
        return false;
    }

    # For demo; get next token; or false
    private function getToken()
    {
        return array_rand([rand(1000, 99999)=>0,rand(1000, 99999)=>0,rand(1000, 99999)=>0,-1=>-1]);
    }

    public function process(array $params = array())
    {
        echo getmypid() . "(token: ".$this->params['token'] . ") - init" . PHP_EOL;
        sleep(1); # fake some heavy process
        if(!isset($this->params['token'])) {
            echo getmypid() . "(token: ".$this->params['token'] . ") - Do first request here " . PHP_EOL;
            $this->manager->start(new Task(['token'=>'randomTokenFirst'], $this->manager));
        } elseif(isset($this->params['token']) && $this->params['token'] !== -1) {
            $token = $this->getToken();
            echo getmypid() . "(token: ".$this->params['token'] . ") - New token obtained (".$token.") " . PHP_EOL;
            $this->manager->start(new Task(['token'=>$token], $this->manager));
        } else {
            echo getmypid() . "(token: ".$this->params['token'] . ") - Last request done " . PHP_EOL;
        }

        sleep(1); # fake some heavy process

        //Handle request here
        echo getmypid() . "(token: ".$this->params['token'] . ") - Processing " . PHP_EOL;

        return true;
    }
}

$manager->start(new Task(['token'=>0], $manager));

输出将是一些调试信息。 它实际上将从第一个请求(没有令牌)运行到结束:

280004(token: 0) - init
280004(token: 0) - New token obtained (64865) 
280005(token: 64865) - init
280004(token: 0) - Processing 
280005(token: 64865) - New token obtained (68564) 
280006(token: 68564) - init
280005(token: 64865) - Processing 
280006(token: 68564) - New token obtained (54573) 
280007(token: 54573) - init
280006(token: 68564) - Processing 
280007(token: 54573) - New token obtained (63093) 
280008(token: 63093) - init
280007(token: 54573) - Processing 
280008(token: 63093) - New token obtained (89230) 
280010(token: 89230) - init
280008(token: 63093) - Processing 
280010(token: 89230) - New token obtained (82375) 
280011(token: 82375) - init
280010(token: 89230) - Processing 
280011(token: 82375) - New token obtained (-1) 
280012(token: -1) - init
280011(token: 82375) - Processing 
280012(token: -1) - Last request done 
280012(token: -1) - Processing 

使用 cronjobs 的解决方案

我将解释一个简单的概念,应该根据您自己的需要进一步调整。 PHP 官方不包含多线程,因此大多数情况下这些解决方案是使用 cronjobs 实现的。

使用数据库/ json

您可以设置一个数据库表来跟踪请求中的最后一个令牌。 或者,例如,您可以将令牌存储在 json 中。 这将为脚本的下一次执行提供信息。

每 x 秒运行一次 cronjob

您可以每五秒运行一次 cronjob 并从数据库或 json 中读取令牌值:

* * * * * ( php script.php )  
* * * * * ( sleep 5 ; php script.php )  
* * * * * ( sleep 10 ; php script.php )  
* * * * * ( sleep 15 ; php script.php )  
* * * * * ( sleep 20 ; php script.php )  
* * * * * ( sleep 25 ; php script.php ) 
* * * * * ( sleep 30 ; php script.php ) 
* * * * * ( sleep 35 ; php script.php ) 
* * * * * ( sleep 40 ; php script.php ) 
* * * * * ( sleep 45 ; php script.php ) 
* * * * * ( sleep 50 ; php script.php ) 
* * * * * ( sleep 55 ; php script.php ) 

附加逻辑

您可以通过在数据库中实现计数器或布尔值以及令牌值来防止 cronjob 执行相同的令牌两次。 并且您应该提供一些逻辑来知道何时到达终点(下一个请求不再返回令牌)。

切换到 Java

如果您想限制同时发出的请求数,我的建议是切换到 Java。 PHP 本身并不支持多线程,因此缺少很多 Java 中包含的特性。 例如,Java Semaphore 对象可以根据许可数量限制同时对特定资源完成的请求数量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM