質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • PHP

    24011questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • MySQL

    6994questions

    MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

  • CakePHP

    2519questions

    CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

CakePHPでマルチプロセスがうまく動きません。

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,498

noripi

score 22

CakePHPのコンポーネントで時間のかかる処理をマルチプロセスにしたいのですが、一番最初のfor文のループが2回目になると下記エラーとなってしまいます。
マルチプロセスの処理を入れないと正常に動作します。
また、my.cnfのクエリの最大サイズやタイムアウトなどを変更してみましたがダメでした。
あまりサーバーサイドは得意ではないのでアドバイスお願いします。

《エラー》

Warning Error: Error while sending QUERY packet. PID=27017 in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Error: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
#0 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(468): PDOStatement->execute(Array)
#1 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(434): DboSource->_execute('SELECT `Crawlin...', Array)
#2 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(671): DboSource->execute('SELECT `Crawlin...', Array, Array)
#3 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(1120): DboSource->fetchAll('SELECT `Crawlin...', false)
#4 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Model.php(3038): DboSource->read(Object(CrawlingContent), Array)
#5 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Model.php(3010): Model->_readDataSource('all', Array)
#6 /home/scraper/public_html/ec_scraper/Controller/Component/ScrapingComponent.php(1414): Model->find('all', Array)
#7 /home/scraper/public_html/ec_scraper/Controller/Component/ScrapingComponent.php(36): ScrapingComponent->crawler_index_multi()
#8 /home/scraper/public_html/ec_scraper/Console/Command/ScraperShell.php(65): ScrapingComponent->test()
#9 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/Shell.php(458): ScraperShell->test()
#10 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(212): Shell->runCommand('test', Array)
#11 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(66): ShellDispatcher->dispatch()
#12 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/cake.php(54): ShellDispatcher::run(Array)
#13 {main}

エラー部分のコード

//データを分割取得
        for ($page=1; $page <= $totalPage; $page++) { 

                $result = $CrawlingContent->find('all', array(
                    'conditions' => $conditions,
                    'recursive' => -1,
                    'limit' => $limit,
                    'page' => $page,
                ));

            if ($limit > ($count - $totalAccess)) {
                $limit = $count - $totalAccess;
            }

            //初期化
            $mkp = $pcount;
            //取得データ
            for ($access=0; $access < $limit;) {

                if ($mkp > ($limit - $access)) {
                    $mkp = $limit - $access;
                }

                for ($i=0; $i < $mkp; $i++) {
                    $pid = pcntl_fork();
                    if( $pid === -1 ) {
                        debug('マルチプロセスのforkに失敗');
                        exit;
                    }elseif($pid === 0){
                        //子プロセス
                        debug($result[$i]['CrawlingContent']['url']);
                        exit;
                    }
                    // 親プロセスの処理
                    $processes[] = $pid;
                }

                foreach($processes as $process) {
                    pcntl_waitpid($process, $status);
                }

                $access = $access + $i;
                $totalAccess = $totalAccess + $i;
            }
}

子プロセスでのDB接続を試してみましたがダメでした。

for ($i=0; $i < $pcount; $i++) {
            $pid = pcntl_fork();
            if( $pid === -1 ) {
                debug('マルチプロセスのforkに失敗');
                exit;
            }elseif($pid === 0){
                //子プロセス
                if ($i===($pcount-1)) {
                    $endPage = ($endPage - $processPage) + $lastProcessPage;
                }

                $page = $startPage;

                for ($page; $page <= $endPage; $page++) { 

                    $result = $CrawlingContent->find('all', array(
                            'conditions' => $conditions,
                            'recursive' => -1,
                            'limit' => $limit,
                            'page' => $page,
                        ));
                    foreach ($result as $resultValue) {
                        debug($resultValue['CrawlingContent']['url']);
                    }

                }
                exit;
            }
            // 親プロセスの処理
            $processes[] = $pid;
            $startPage = $startPage + $processPage;
            $endPage = $endPage + $processPage;
        }

        foreach($processes as $process) {
            pcntl_waitpid($process, $status);
        }


エラーコード

Warning Error: Packets out of order. Expected 1 received 3. Packet size=262144 in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Warning Error: Packets out of order. Expected 4 received 97. Packet size=7496534 in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Warning Error: Packets out of order. Expected 1 received 99. Packet size=6621798 in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Warning Error: PDOStatement::execute(): MySQL server has gone away in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Warning Error: PDOStatement::execute(): MySQL server has gone away in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Warning Error: PDOStatement::execute(): MySQL server has gone away in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Warning Error: PDOStatement::execute(): Error reading result set's header in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Warning Error: PDOStatement::execute(): Error reading result set's header in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468]

Error: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
#0 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(468): PDOStatement->execute(Array)
#1 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(434): DboSource->_execute('SELECT `Crawlin...', Array)
#2 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(671): DboSource->execute('SELECT `Crawlin...', Array, Array)
#3 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(1120): DboSource->fetchAll('SELECT `Crawlin...', false)
#4 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Model.php(3038): DboSource->read(Object(CrawlingContent), Array)
#5 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Model.php(3010): Model->_readDataSource('all', Array)
#6 /home/scraper/public_html/ec_scraper/Controller/Component/ScrapingComponent.php(1434): Model->find('all', Array)
#7 /home/scraper/public_html/ec_scraper/Controller/Component/ScrapingComponent.php(36): ScrapingComponent->crawler_index_multi()
#8 /home/scraper/public_html/ec_scraper/Console/Command/ScraperShell.php(65): ScrapingComponent->test()
#9 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/Shell.php(458): ScraperShell->test()
#10 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(212): Shell->runCommand('test', Array)
#11 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(66): ShellDispatcher->dispatch()
#12 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/cake.php(54): ShellDispatcher::run(Array)
#13 {main}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+2

子プロセスが終了した時点で、MySQLのコネクションが破棄されてしまうようですね。
以下、参考になりそうな文献

※Cakephpの内部ではPDOが使われており、一つのコネクションでSQLを発行しているはずです。たぶんCakephpないでpcntl_forkを使う事は想定されていないので、仕方ない気がします...。もしかしたら、子プロセス内で初めてSQLを実行すれば、子プロセス内でコネクションが作られるので、問題なく動くかもしれません(要件を満たすかはわかりませんが)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/02 09:45

    ご回答ありがとうございます。
    icchiiさんとA.lchiさんのご回答を参考に子プロセスでそれぞれDB接続するようにして試してみます。

    キャンセル

  • 2016/11/02 09:57 編集

    時間のかかる処理というのが具体的に何かわからないので的確なコメントはできませんが...
    なるべくマルチプロセス化以外の方法を模索したほうがいいかもしれませんよ。
    ※PHPで1リクエストの処理をマルチプロセスで処理するなんて聞かないですし...。
    自分だったら、バッチで事前に時間のかかる処理を実行した結果をDB等に持っておくとかよくやります。

    キャンセル

  • 2016/11/02 13:22

    子プロセスにて試してみましたがダメでした。。。
    内容はDB内にある大量のテキストデータの一部を抜き出した結果を別テーブルに格納するものです。
    時間的制約と他言語の知識不足、もともとCakePHPで組まれていたため、このままCakePHPでなんとかならないかなって感じでした。。。

    キャンセル

  • 2016/11/02 13:28

    事前に抜き出しておくとかではイケないのですか? 毎回抜き出すキーワードが違うとか?

    キャンセル

  • 2016/11/02 14:10

    一度に抜き出そうとするとメモリ不足となってしまうので、メモリ不足にならないギリギリのところで、リミットをかけて分割取得しています。。。
    もちろん。php.iniでメモリサイズは変更済みです。

    キャンセル

  • 2016/11/02 14:13

    はい。処理によって抜き出すキーワードはかわります。

    キャンセル

  • 2016/11/02 14:19

    メモリ不足が問題なら、マルチプロセスである必要はなくて、単にfindするときにpageとlimitで件数を絞って処理すればいいだけな気がしますよ!!

    キャンセル

  • 2016/11/02 15:00

    はい。メモリ不足に関してはpageとlimitでの対処を検討してました。
    一度、シングルで実行時間のテスト結果から総合的な実行時間を計算した結果、実用的な状態ではなかったので、今回マルチプロセスの選択となりました。。。

    キャンセル

  • 2016/11/02 15:10

    なるほど!
    時間がかかっているのがCPUボトルネックだったら、マルチプロセスにすればCPUの数だけ早くはなりますが、1リクエストでCPUを全部専有するような処理では実用性は怪しい気もしますが...まぁ利用者が限定的だったらそれでもいいのかもしれませんが。
    根本的には全文検索システムとか導入するとかしないとなんとも言えない気がしました。(使っているならチューニングとか)

    キャンセル

  • 2016/11/02 15:50

    はい。利用者は基本1名で操作時間も限定的で集計したデータを翌日見る感じです。
    なるほど。全文検索システム未導入でした。既存(CakePHP)のシステムにも導入は可能なのでしょうか。
    また、今回の質問の件で色々調べたのですが、PDOが怪しそうです。なにか設定とかご存知ではないでしょうか。

    キャンセル

  • 2016/11/02 16:15 編集

    > なるほど。全文検索システム未導入でした。既存(CakePHP)のシステムにも導入は可能なのでしょうか。
    MySQLは標準で全文検索インデックスをサポートしています。SQLが変わりますが、Cakephpからでも実行は可能です。
    https://dev.mysql.com/doc/refman/5.6/ja/fulltext-search.html
    また、日本語向けに改善されたmroongaというライブラリもあります。
    http://mroonga.org/ja/

    > また、今回の質問の件で色々調べたのですが、PDOが怪しそうです。なにか設定とかご存知ではないでしょうか。

    そうですね。Cakephpでは内部的にはPDOを使っており、そのPDOのコネクションが子プロセスにも渡されており、子プロセス終了時にコネクションが切れているんだと思います。
    ちょっと調べてみましたが、以下のStackover flowによれば、reconnectという関数があるようなので、findする前に毎回reconnectすればうまくいくかもしれません。
    http://stackoverflow.com/questions/10051295/mysql-server-has-gone-away-in-cakephp-bake-console
    ※実際Cake2のコードをみるとたしかにそういう関数は存在していました。使ったことはないですが、試してみてください。

    キャンセル

  • 2016/11/02 17:08

    お付き合い頂きありがとうございます。
    またまた、大変勉強になります。
    「reconnect」ですが、なんだかいけそうな匂いがしますね!
    早速、試させていただきます。

    キャンセル

  • 2016/11/02 22:06

    find前にreconnectすることで見事に解決しました。本当にありがとうございました。

    キャンセル

  • 2016/11/03 07:46

    ちょっと予想外の展開でしたが、うまくいってよかったです!
    結果、処理は実用的なレベルになったか心配ですが....

    キャンセル

  • 2016/11/04 10:00

    ご心配頂きありがとうございます。
    その後、実行時間のテストをしましたが、シングル時の半分以下の時間で完了しました。
    今回の処理では、効果有りのようです。

    キャンセル

  • 2016/11/04 10:15

    ちなみに、ですがreconnectの前に一度接続の確認を入れたほうがいいのでしょうか。
    また、必要な場合、CakePHPに接続を確認するような関数はご存知ですか。検索してみましたがそれらいしものが見当たりませんでした。
    サンプルではmysql_pingを使っていましたが現在非推奨のようで、PDOを使えと書いてあります。。。

    キャンセル

  • 2016/11/04 10:29 編集

    おお、改善されて良かったです!!

    マルチプロセス化すれば確実にコネクション切れるので、確認しなくてもいいと思いますよ。
    またreconnect()関数は、DB設定を動的に変更して、再接続したいときに使うもののようなので、内部的にはdisconnectしてからconnectしているので、接続が切れていないくても正常に動きます。
    ※Cakeのコードを見て確認しただけなので、動作確認は別途やってください。

    なお、getConnection()関数で、PDOの生コネクションを取得できるので、そこから確認することもできると思いますけど

    キャンセル

  • 2016/11/04 10:53

    なるほど。そうなのですね。
    大変助かりました。本当にありがとうございます。感謝です。

    キャンセル

+1

データベースでエラーが発生している様ですので、子プロセスには、データベースへのアクセスが行われた中で発生している様に見れます。

一般的にforkされる場合、自身のコピーを作成してサブミットし、同ステップより開始されるイメージです。
その際、親から切り離され際にメモリはコピーはされますが、共有はできない状態となります。

親プロセスでDB接続をオープンしたままの状態で子プロセスを生成してしまうと、子プロセスで接続ファイルIDが同じものを使ってしまい、1つの子プロセスが終了すると、他の子プロセスのDB接続がクローズされてしまいます。
ですのでフォークする場合、各子プロセスで新たにDB接続してアクセスを行います。

CakePHPフレームワークの知識が無いので、DBへの接続が個々に行えるのかが分かりません。(調べてみます)

phpのつたない例ですが

for ($i = 1; $i <= 5; ++$i) {
        $pid = pcntl_fork();
        if (!$pid) {
            sleep(1);
            print "In child $i\n";

            try{
                $dbh = new PDO($dsn, $user, $password);
            }catch (PDOException $e){
                print('Connection failed:'.$e->getMessage());
                die();
            }
            $sql = 'select * from tableA';
            $stmt = $dbh->query($sql);

            while($result = $stmt->fetch()){
                    print($result['UserID']." ");
                    print($result['station_id1']."\n");
            }

            exit($i);
        }
    }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/02 09:41

    ご回答ありがとうございます。
    なるほど、勉強になりますl。となると、icchiiさんもおっしゃられてたように、子プロセスでそれぞれDB接続してデータを取得するようにして試してみます。

    キャンセル

  • 2016/11/02 13:15

    子プロセスにて試してみましたがダメでした。。。

    キャンセル

  • 2016/11/02 13:37

    CakePHPがよく読めていませんで申し訳ありませんが、//子プロセス以下の場所で、DB接続が新たにされているとの認識でよろしいでしょうか?

    キャンセル

  • 2016/11/02 14:04

    はい。その通りです。

    キャンセル

0

ちょっとアレな回答ですが。
私は、curl_multiで自ホストにAPIコールという形でマルチリクエストを投げる形で解決したことがあります。
この場合、HTTPリクエスト/レスポンスによるオーバーヘッドは必要になりますが、問題なく並列処理は可能です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/02 15:11

    ご回答ありがとうございます。
    curl_multiですが、私の認識ではHTTPのリクエストを並列化するものだと思っていました。
    今回は時間のかかるテキスト解析をマルチプロセスにより、短縮したいというものでした。なにか、使いかたがあるのでしょうか。
    もし、参考サイトなどありましたらお知らせ頂ければ幸いです。

    キャンセル

  • 2016/11/02 16:10

    「HTTPのリクエストを並列化するもの」
    その通りですよ。
    自分のサイトに対して、リクエストを複数投げる感じです。
    そのテキスト解析を、例えば1MB毎にデータを分割して、それぞれのリクエストで分割したデータ一つずつ解析し、結果をリクエスト元でまとめるというような。

    キャンセル

  • 2016/11/02 17:02

    なるほど。そのような発想はありませんでした。大変勉強になります。
    この方法なら、確かにいけそうですね。

    キャンセル

同じタグがついた質問を見る

  • PHP

    24011questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • MySQL

    6994questions

    MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

  • CakePHP

    2519questions

    CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。