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

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

ただいまの
回答率

90.04%

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

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,045

noripi

score 28

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/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

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

    キャンセル

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

  • ただいまの回答率 90.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる