简体   繁体   English

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

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

I have a page to run with cron.我有一个用 cron 运行的页面。 The function on this page sends a request to a link and the sample json returned from this request is below.此页面上的函数向链接发送请求,该请求返回的示例 json 如下所示。

First Request

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

Second Request

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

The main thing I want to do here is to be able to send another request without waiting for the actions to be taken after the previous request, by getting the next value returned after the first request.我在这里要做的主要事情是能够发送另一个请求,而无需等待在前一个请求之后采取的操作,方法是在第一个请求之后返回下一个值。 So, let me show you with an example of a function:因此,让我通过一个函数示例向您展示:

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

I want to add or update the returned items to the database at the same time by running the above function 3 times at the same time.我想通过同时运行上述函数3次同时将返回的项目添加或更新到数据库中。 So, I want to perform 3 transactions at the same time.所以,我想同时执行 3 个事务。 Is it possible to do this with threads?是否可以用线程来做到这一点? Or is there another way?还是有其他方法? I tried to do it using the header('Location:...') function, but after a while it gave an error saying too_many_redirects.我尝试使用 header('Location:...') 函数来执行此操作,但过了一会儿它给出了一个错误,说 too_many_redirects。

There will be about 1 million+ data and each request will do 200 updates.将有大约 100 万+个数据,每个请求将进行 200 次更新。 So, since I want to run it 3 times here, I will have made 600 updates at once.因此,由于我想在这里运行 3 次,我将一次进行 600 次更新。

I offer two solutions for approaching this.我提供了两种解决方案来解决这个问题。 One is using multithreading and one is using cronjobs.一种是使用多线程,一种是使用 cronjobs。

Solution using threads in PHP在 PHP 中使用线程的解决方案

There are libraries for supporting multithreading in PHP. PHP中有一些支持多线程的库。

Install PCNTL and alfallouji/php_multithread安装 PCNTL 和alfallouji/php_multithread

You can install this multithreading library with:您可以使用以下命令安装此多线程库:

composer require alfallouji/php_multithread

Which depends on PCNTL which comes with the following package:这取决于带有以下软件包的 PCNTL:

sudo apt-get install php-cli

Concept概念

  • (1) Run first request without token (1) 无令牌运行第一个请求
  • (1) Get response with next token (1) 使用下一个令牌获取响应
  • (1) Start new thread with new token (1) 使用新令牌启动新线程
    • (2) Run second request with new token (2) 使用新令牌运行第二个请求
    • (2) Get response with next token (2) 使用下一个令牌获取响应
    • (2) Start new thread with new token (2) 使用新令牌启动新线程
    • ... third request etc.... ...第三个请求等...
    • (2) Process second request (2) 处理第二个请求
  • (1) Process first request (1) 处理第一个请求

Example using alfallouji/php_multithread使用alfallouji/php_multithread

This code is a simulation of the concept explained above.此代码是对上述概念的模拟。 Add the following code to test.php, and run php test.php :将以下代码添加到 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));

The output will be some debug information.输出将是一些调试信息。 It will virtually run from the first request (without token) to the end:它实际上将从第一个请求(没有令牌)运行到结束:

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 

Solution using cronjobs使用 cronjobs 的解决方案

I will explain a simple concept which should be tweaked further to your own needs.我将解释一个简单的概念,应该根据您自己的需要进一步调整。 PHP does officially not include multithreading, so most often these solutions are implemented using cronjobs. PHP 官方不包含多线程,因此大多数情况下这些解决方案是使用 cronjobs 实现的。

Using a database/ json使用数据库/ json

You could set up a database table which keeps track of the last token from the request.您可以设置一个数据库表来跟踪请求中的最后一个令牌。 Or you could store the token in a json for example.或者,例如,您可以将令牌存储在 json 中。 This will provide the information to the next execution of the script.这将为脚本的下一次执行提供信息。

Running the cronjob every x seconds每 x 秒运行一次 cronjob

You could run a cronjob every five seconds and read the token value from the database or json:您可以每五秒运行一次 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 ) 

Additional logic附加逻辑

You could prevent the cronjob from executing the same token twice, by implementing a counter or a boolean value in the database along with the token value.您可以通过在数据库中实现计数器或布尔值以及令牌值来防止 cronjob 执行相同的令牌两次。 And you should provide some logic to know when the end is reached (no more tokens are returned for the next request).并且您应该提供一些逻辑来知道何时到达终点(下一个请求不再返回令牌)。

Switching to Java切换到 Java

If you want to limit the number of requests made at the same time, my advice is to switch to Java.如果您想限制同时发出的请求数,我的建议是切换到 Java。 PHP does not natively support multithreading, so if lacks a lot of features that are included in Java. PHP 本身并不支持多线程,因此缺少很多 Java 中包含的特性。 For example a Java Semaphore object can limit the number of requests done, based on a number of permits, at the same time to a specific resource.例如,Java Semaphore 对象可以根据许可数量限制同时对特定资源完成的请求数量。

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

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