4

これは、私が解決策に取り組むときに使用しているコードです。

 public function indexAction()
    {
        //id3 options
        $options = array("version" => 3.0, "encoding" => Zend_Media_Id3_Encoding::ISO88591, "compat" => true);
        //path to collection
        $path = APPLICATION_PATH . '/../public/Media/Music/';//Currently Approx 2000 files
        //inner iterator
        $dir = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
        //iterator
        $iterator = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::SELF_FIRST);
        foreach ($iterator as $file) {
            if (!$file->isDir() && $file->getExtension() === 'mp3') {
                //real path to mp3 file
                $filePath = $file->getRealPath();
                Zend_Debug::dump($filePath);//current results: accepted path no errors
                $id3 = new Zend_Media_Id3v2($filePath, $options);
                foreach ($id3->getFramesByIdentifier("T*") as $frame) {
                    $data[$frame->identifier] = $frame->text;
                }
                Zend_Debug::dump($data);//currently can scan the whole collection without timing out, but APIC data not being processed.
            }
        }
    }

問題:複数のディレクトリにあるmp3ファイルのファイルシステムを処理します。id3タグデータをデータベース(3つのテーブル)に抽出し、カバー画像をタグから別のファイルに抽出します。

実際の抽出とデータ処理を処理できます。私の問題は出力にあります。

Zend Framework 1.xが出力バッファリングを処理する方法では、ファイルが処理されていることを示すインジケーターを出力することは困難です。古いスタイルのPHPスクリプトでは、出力バッファリングを使用せずに、ループを繰り返すたびにHTMLを少し出力して、進行状況を示すことができます。

各アルバムのディレクトリを処理し、結果を出力してから、次のアルバムのディレクトリに進むことができるようにしたいと思います。特定のエラーに対してのみユーザーの介入が必要です。

どんな助けでもいただければ幸いです。

Javascriptは私が探しているソリューションではありません。これは、PHPとZF1MVCの構成内で可能になるはずだと思います。

私は主に私自身の啓蒙のためにこれを行っています、それはいくつかの重要な概念を学ぶための非常に良い方法のようです。

[編集]
わかりました。これを小さなチャンクに分割する方法についてのアイデアはどうですか。1つのチャンクを処理し、コミットし、次のチャンクを処理します。ZFの内外。

[編集]
私が達成しようとしていることに問題が見られ始めています。出力バッファリングはZFで発生しているだけでなく、ZFからブラウザに至るまであらゆる場所で発生しているようです。うーん...

4

4 に答える 4

7

序章

これは、やってはいけないことの典型的な例です。

  • 遅い PHP で解析ID3 tagしようとしていて、一度に複数の解析ファイルを作成しようとすると、確実にさらに遅くなります。

  • RecursiveDirectoryIteratorフォルダーとサブフォルダー内のすべてのファイルを、私が見たものからロードします。制限はありません..2,000今日、100,000翌日になる可能性がありますか? 合計処理時間は予測できず、場合によっては数時間かかることもあります

  • 単一ファイル システムへの依存度が高い。現在のアーキテクチャでは、ファイルがローカル システムに保存されているため、ファイルを分割して適切な負荷分散を行うことが困難です。

  • ファイル情報が以前に抽出されたかどうかを確認していないため、この結果になりますLoop and extraction Duplication

  • No locking system.. これは、このプロセスが同時に開始される可能性があることを意味し、その結果、サーバーのパフォーマンスが全体的に低下します。

解決策 1 : 現在のアーキテクチャを使用

私のアドバイスは、ファイルをまとめて使用しloopたり 処理したりしないことです。RecursiveDirectoryIterator

サーバーにアップロードまたは転送されたらすぐにファイルをターゲットにします。そうすれば、一度に 1 つのファイルだけを操作して、処理時間を分散させることができます。

解決策 2: ジョブ キュー (提案された解決策)

あなたの問題はまさにジョブキューが行うように設計されているPHPことです。また、 ..を利用して、 CまたはC++パフォーマンスのために解析を実装することに限定されません

アドバンテージ

  • 作業を行うのにより適した他のマシンまたはプロセスにジョブを転送する
  • 並行して作業を行い、処理の負荷を分散することができます
  • 時間のかかるタスクを非同期で実行することにより、大量の Web アプリケーションでのページ ビューの待ち時間を短縮します。
  • PHPサーバー内の複数言語クライアントC

例はテスト済みです

期待されるプロセス クライアント

  • ジョブ キューに接続 例 ドイツ語
  • MongoDB や Redis などのデータベースに接続する
  • フォルダー パスでループする
  • ファイル拡張子を確認する
  • ファイルが mp3 の場合、ファイル ハッシュを生成します。sha1_file
  • ファイルが処理のために送信されたかどうかを確認する
  • Job Server にハッシュ、ファイルを送信

予想されるプロセス サーバー

  • ジョブ キューに接続 例 ドイツ語
  • MongoDB や Redis などのデータベースに接続する
  • ハッシュ/ファイルを受け取る
  • ID3 タグを抽出します。
  • ID3タグ情報でDBを更新

最後に、この処理は複数のサーバーで並行して実行できます

于 2012-11-15T08:06:26.163 に答える
2

1 つの解決策は、Gearman などのジョブ キューを使用することです。Gearman は、この種の問題に対する優れたソリューションであり、Zend Framework (http://blog.digitalstruct.com/2010/10/17/integrating-gearman-into-zend-framework/) と簡単に統合できます。

各「チャック」を処理するワーカーを作成できるため、ジョブの処理中にプロセスがブロックされずに続行できるため、音楽/画像処理などの長時間実行されるプロセスに非常に便利ですhttp://gearman.org/index. php?id=getting_started

于 2012-11-13T16:03:06.517 に答える
1

プラグインの使用をお勧めします。

class Postpone extends Zend_Controller_Plugin_Abstract
{

    private $tail;

    private $callback;


    function __construct ($callback = array())
    {
        $this->callback = $callback;
    }


    public function setRequest (Zend_Controller_Request_Abstract $request)
    {
        /*
         * We use layout, which essentially contains some html and a placeholder for action output.
         * We put the marker into this placeholder in order to figure out "the tail" -- the part of layout that goes after placeholder.
         */
        $mark = '---cut-here--';
        $layout = $this->getLayout ();

        $layout->content = $mark;

        /*
         * Now we have it.
         */
        $this->tail = preg_replace ("/.*$mark/s", '', $layout->render ());
    }


    public function postDispatch (Zend_Controller_Request_Abstract $request)
    {
        $response = $this->getResponse ();

        $response->sendHeaders ();

        /*
         * The layout generates its output to the default section of the response.
         * This output inludes "the tail".
         * We don't need this tail shown right now, because we have callback to do.
         * So we remove it here for a while, but we'll show it later.
         */
        echo substr ($this->getResponse ()
            ->getBody ('default'), 0, - strlen ($this->tail));

        /*
         * Since we have just echoed the result, we don't need it in the response. Do we?
         */
            Zend_Controller_Front::getInstance ()->returnResponse(true);
        $response->clearBody ();

        /*
         * Now to business.
         * We execute that calculation intensive callback.
         */
        if (! empty ($this->callback) && is_callable ($this->callback))
        {
            call_user_func ($this->callback);
        }

        /*
         * We sure don't want to leave behind the tail.
         * Output it so html looks consistent.
         */
        echo $this->tail;
    }


    /**
     * Returns layout object
     */
    function getLayout ()
    {
        $layout_plugin = Zend_Controller_Front::getInstance ()->getPlugin ('Zend_Layout_Controller_Plugin_Layout');
        return $layout = $layout_plugin->getLayout ();
    }
}




class IndexController extends Zend_Controller_Action
{


    /*
     * This is a calculation intensive action
     */
    public function indexAction ()
    {
        /*
         * Zend_Layout in its current implementation accumulates whole action output inside itself.
         * This fact hampers out intention to gradually output the result.
         * What we do here is we defer execution of our intensive calculation in form of callback into the Postpone plugin.
         * The scenario is:
         * 1. Application started
         * 2. Layout is started
         * 3. Action gets executed (except callback) and its output is collected by layout.
         * 4. Layout output goes to response.
         * 5. Postpone::postDispatch outputs first part of the response (without the tail).
         * 6. Postpone::postDispatch calls the callback. Its output goes stright to browser.
         * 7. Postpone::postDispatch prints the tail.
         */
        $this->getFrontController ()
            ->registerPlugin (new Postpone (function  ()
        {
            /*
             * A calculation immigration
             * Put your actual calculations here.
             */
        echo str_repeat(" ", 5000);
        foreach (range (1, 500) as $x)
        {
            echo "<p>$x</p><br />\n";
            usleep(61500);
            flush();
        }
        }), 1000);
    }
}
于 2012-11-13T19:51:25.783 に答える
1

Zend Framework の仕組みに詳しくありません。一般的なアドバイスをします。非常に多くの反復を実行し、場合によっては長時間かかるプロセスで作業する場合、通常、長いプロセスをバックグラウンド プロセスに移動することをお勧めします。または、Web関連では、cronジョブに移動しました。

使用したいプロセスが単一のサイト用である場合は、cronjob で次のようなものを実装できます (注: 大まかな疑似コード):

<?php

$targetdir = "/path/to/mp3";
$logdir = "/path/to/log/";

//check if current state is exists. If it does, then previous cronjob is still running
//we should stop this process so that it doesn't do duplicated process which might have introduced random bugs
if(file_exists($logdir."current-state")){
    exit;
}

//start process, write state to logdir
file_put_contents($logdir."current-log", "process started at ".date("Y-m-d H:i:s"));
file_put_contents($logdir."current-state", "started\t".date("Y-m-d H:i:s"));
$dirh = opendir($targetdir);
while($file = readdir($dirh)){
    //lets ignore current and parent dir
    if(in_array($file, array('.', '..'))) continue;

    //do whatever process you want to do here:


    //you might want to write another log, too:
    file_put_contents($logdir."current-log", "processing file {$file}", FILE_APPEND);


}
closedir($dirh);
file_put_contents($logdir."current-log", "process finished at ".date("Y-m-d H:i:s"));

//process is finished, delete current-state:
unlink($logdir."current-state");

次に、Web 用の php ファイルにスニペットを追加して、管理ページ、フッター、または任意のページに進行状況を表示できます。

<?php

if(file_exists($logdir."current-state")){
    echo "<strong>there are background process running.</strong>";
} else {
    echo "<strong>no background process running.</strong>";
}
于 2012-11-13T05:54:47.607 に答える