簡體   English   中英

加速PHP應用程序

[英]Speeding up a PHP App

我有一個需要處理的數據列表。 它現在的工作方式是這樣的:

  • 用戶單擊進程按鈕。
  • PHP代碼獲取需要處理的第一個項目,需要15-25秒來處理它,移動到下一個項目,依此類推。

這需要太長時間。 我想要的是:

  • 用戶單擊過程按鈕。
  • PHP腳本獲取第一個項目並開始處理它。
  • 同時,腳本的另一個實例接受下一個項目並對其進行處理。
  • 等等,所以大約5-6個項目正在同時處理,我們在15-25秒內處理了6個項目而不是一個。

這樣的事情可能嗎?

我以為我每秒都使用CRON來啟動腳本實例。 所有需要處理的項目都將在MySQL數據庫中標記為這樣,因此每當通過CRON啟動實例時,它只會將標記的下一個項目處理並刪除該標志。

思考?

編輯:為了澄清一些事情,每個'item'作為單獨的行存儲在mysql數據庫表中。 每當處理項開始處理時,它都被標記為在db中處理,因此每個新實例將只抓取未處理的下一行並處理它。 因此,我不必將這些項目作為命令行參數提供。

這是一個解決方案,而不是最好的解決方案,但在Linux上運行良好:

將處理PHP拆分為單獨的CLI腳本,其中:

  • 命令行輸入包括`$ id`和`$ item`
  • 該腳本將其PID寫入`/ tmp / $ id。$ item.pid`中的文件
  • 腳本echos結果為XML或可以讀入PHP到stdout的東西
  • 完成后,腳本將刪除`/ tmp / $ id。$ item.pid`文件

您的主腳本(可能在您的網絡服務器上)會:

  • `exec(“nohup php myprocessing.php $ id $ item> /tmp/$id.$item.xml”);`對於每個項目
  • 輪詢`/ tmp / $ id。$ item.pid`文件,直到刪除所有文件(睡眠/檢查輪詢就夠了)
  • 如果永遠不會刪除它們,請刪除所有處理腳本並報告失敗
  • 如果成功,請從`/ tmp / $ id。$ item.xml`中讀取格式/輸出到用戶
  • 如果您不想緩存以供以后使用,請刪除XML文件

一個后台的nohup啟動應用程序將獨立於啟動它的腳本運行。

這讓我很感興趣,我決定寫一個POC。

test.php的

<?php
$dir =  realpath(dirname(__FILE__));
$start = time();

// Time in seconds after which we give up and kill everything
$timeout = 25;

// The unique identifier for the request
$id = uniqid();

// Our "items" which would be supplied by the user
$items = array("foo", "bar", "0xdeadbeef");

// We exec a nohup command that is backgrounded which returns immediately
foreach ($items as $item) {
    exec("nohup php proc.php $id $item > $dir/proc.$id.$item.out &");
}

echo "<pre>";
// Run until timeout or all processing has finished
while(time() - $start < $timeout) 
{
  echo (time() - $start), " seconds\n";
  clearstatcache();    // Required since PHP will cache for file_exists
  $running = array();
  foreach($items as $item)
  {
      // If the pid file still exists the process is still running    
      if (file_exists("$dir/proc.$id.$item.pid")) {
          $running[] = $item;
      }
  }
  if (empty($running)) break;
  echo implode($running, ','), " running\n";
  flush();
  sleep(1);  
}

// Clean up if we timeout out
if (!empty($running)) {
    clearstatcache();
    foreach ($items as $item) {
        // Kill process of anything still running (i.e. that has a pid file)
        if(file_exists("$dir/proc.$id.$item.pid") 
            && $pid = file_get_contents("$dir/proc.$id.$item.pid")) {
            posix_kill($pid, 9);                
            unlink("$dir/proc.$id.$item.pid");
            // Would want to log this in the real world
            echo "Failed to process: ", $item, " pid ", $pid, "\n";
    }
    // delete the useless data
    unlink("$dir/proc.$id.$item.out");
    }
} else {
    echo "Successfully processed all items in ", time() - $start, " seconds.\n";
    foreach ($items as $item) {
    // Grab the processed data and delete the file
        echo(file_get_contents("$dir/proc.$id.$item.out"));
        unlink("$dir/proc.$id.$item.out");
    }
}
echo "</pre>";
?>

proc.php

<?php
$dir =  realpath(dirname(__FILE__));
$id = $argv[1];
$item = $argv[2];

// Write out our pid file
file_put_contents("$dir/proc.$id.$item.pid", posix_getpid());

for($i=0;$i<80;++$i)
{
    echo $item,':', $i, "\n";
    usleep(250000);
}

// Remove our pid file to say we're done processing
unlink("proc.$id.$item.pid");

?>

將test.php和proc.php放在服務器的同一文件夾中,加載test.php並享受。

你當然需要nohup(unix)和PHP cli來實現這一點。

很多樂趣,我可能會在以后找到它的用途。

使用像Beanstalkd這樣的外部工作隊列,你的PHP腳本也會編寫一堆作業。 您有盡可能多的工作進程從beanstalkd中提取作業並盡快處理它們。 你可以像擁有內存/ CPU那樣增加工作量。 您的工作機構應該包含盡可能少的信息,可能只是您點擊數據庫的一些ID。 beanstalkd有一大堆客戶端API,本身有一個非常基本的API,想想memcached。

我們使用beanstalkd來處理我們所有的后台工作,我喜歡它。 易於使用,速度非常快。

PHP中沒有多線程,但是你可以使用fork。

php.net:pcntl-fork

或者您可以執行system()命令並啟動另一個多線程進程。

你可以在客戶端的javascript中實現線程嗎? 在我看來,我已經看到了一個實現它的javascript庫(來自谷歌?)。 谷歌它,我相信你會找到一些東西。 我從來沒有做過,但我知道它可能。 無論如何,你的客戶端javascript可以為單獨的線程中的每個項激活(ajax)一個php腳本。 這可能比嘗試在服務器端完成所有操作更容易。

-don

如果您正在運行高流量的PHP服務器,那么如果您不使用備用PHP緩存,那么您就是INSANEhttp//php.net/manual/en/book.apc.php 您不必進行代碼修改即可運行APC。

另一個可以與APC一起使用的有用技術是使用Smarty模板系統,它允許您緩存輸出,以便不必重建頁面。

為了解決這個問題,我使用了兩種不同的產品; Gearman和RabbitMQ。

將您的工作放入某些排隊軟件(如Gearman或Rabbit)的好處是,您擁有多台計算機,他們都可以參與處理隊列中的項目。

Gearman更容易設置,所以我建議稍微先探討一下。 如果你發現你需要更重要的東西和隊列穩健性; 看看RabbitMQ

您可以使用pcntl_fork()和family來分叉進程 - 但是您可能需要像IPC這樣的東西來回傳給子進程(您分叉的那個)完成的父進程。

您可以讓它們寫入共享內存,例如通過內存緩存或數據庫。

您還可以讓子進程將已完成的數據寫入文件,父進程一直在檢查 - 當每個子進程完成時,創建/寫入/更新文件,父進程可以一次抓取一個,並且他們把它們扔回被叫者/客戶端。

父母的工作是控制隊列,確保相同的數據不被處理兩次,並且還要檢查孩子們的健全狀況(更好地殺死那個失控過程並重新開始等等)

要記住的其他事項 - 在Windows平台上你將受到嚴重限制 - 我甚至認為你無法訪問pcntl_,除非你編譯PHP並支持它。

此外,您可以在處理完數據后對其進行緩存,還是每次都是唯一數據? 這肯定會加快速度..?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM