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

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

新規登録して質問してみよう
ただいま回答率
85.48%
cURL

cURLはHTTP, FTPやTelnetなど複数のプロトコルを用いてデータを転送するライブラリとコマンドラインツールを提供します。

PHP

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

Q&A

解決済

2回答

5888閲覧

curl_multi_selectでのレスポンスの取得について

yu-

総合スコア12

cURL

cURLはHTTP, FTPやTelnetなど複数のプロトコルを用いてデータを転送するライブラリとコマンドラインツールを提供します。

PHP

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

0グッド

0クリップ

投稿2016/12/22 11:55

php5.6.24 環境でWEB APIへ複数のデータを一括でPOSTし、更新する処理を作成中です。

参考にしているコードはcurl_multiでHTTP並行リクエストを行うサンプル

curl_multi_select時に一部データがfalseしか取得できない現象が発生し困り果てています。
同一のデータで複数回実行した際にも、一部データのみFALSEになる場合や、全件正常に取得できる場合などもあります。

curl_multi_info_read実行時に複数のデータを掴んでいることもあるとの記事を見つけたのですが、複数のデータを取得する繰り返し処理を下記コードに追加する必要があるでしょうか。

今回、作成している処理では、同一のAPIへCURLOPT_POSTFIELDSに持たせたキー値を元にした更新処理となります。
上記の参考記事以外にもあちこち見ているのですが、基本的に別々のAPI(URL)への接続を想定しており、
同一API(URL)への並列処理を想定したものが見当たりません。

また、根本的に同一のURLへの並列アクセスの場合は、レスポンスが正常に取得できないことなどはあるでしょうか。

よろしくお願いいたします。

php

1<?php 2//タイムアウト時間を決めておく 3$TIMEOUT = 10; //10秒 4 5$mh = curl_multi_init(); 6$url = 'http://example.com/api' 7$header = array("Authorization:Bearer " . $accessToken,); 8 9$i = 0; 10 11foreach($datas as $data) { 12 $params = array( 13 'id' => $data->id, 14 ... 15 ... 16 ... 17 ); 18 19 ${$i} = curl_init($url); 20 curl_setopt_array(${$i}, array( 21 CURLOPT_POST => true, 22 CURLOPT_RETURNTRANSFER => true, 23 CURLOPT_POSTFIELDS => http_build_query($params), 24 CURLOPT_HTTPHEADER => $header, 25 CURLOPT_TIMEOUT => $TIMEOUT, 26 CURLOPT_CONNECTTIMEOUT => $TIMEOUT, 27 CURLOPT_VERBOSE => TRUE, 28 )); 29 30 curl_multi_add_handle($mh, ${$i}); 31 $i++; 32} 33 34do { 35 $stat = curl_multi_exec($mh, $running); //multiリクエストスタート 36} while ($stat === CURLM_CALL_MULTI_PERFORM); 37if ( ! $running || $stat !== CURLM_OK) { 38 throw new RuntimeException('リクエストが開始出来なかった。マルチリクエスト内のどれか、URLの設定がおかしいのでは?'); 39} 40 41do switch (curl_multi_select($mh, $TIMEOUT)) { //イベントが発生するまでブロック 42 case -1: //selectに失敗。ありうるらしい。 https://bugs.php.net/bug.php?id=61141 43 usleep(10); //ちょっと待ってからretry。ここも別の処理を挟んでもよい。 44 do { 45 $stat = curl_multi_exec($mh, $running); 46 } while ($stat === CURLM_CALL_MULTI_PERFORM); 47 continue 2; 48 49 case 0: //タイムアウト -> 必要に応じてエラー処理に入るべきかも。 50 continue 2; //ここではcontinueでリトライします。 51 52 default: //どれかが成功 or 失敗した 53 do { 54 $stat = curl_multi_exec($mh, $running); //ステータスを更新 55 } while ($stat === CURLM_CALL_MULTI_PERFORM); 56 57 do if ($raised = curl_multi_info_read($mh, $remains)) { 58 //変化のあったcurlハンドラを取得する 59 $info = curl_getinfo($raised['handle']); 60 echo "$info[url]: $info[http_code]\n"; 61 $response = curl_multi_getcontent($raised['handle']); 62 63 if ($response === false) { 64 //エラー。404などが返ってきている 65 echo 'ERROR!!!', PHP_EOL; 66 } else { 67 //正常にレスポンス取得 68 //戻り値(オブジェクト)からループでデータを取得する 69 $responseXML = simplexml_load_string($response); 70 71 $result_num = count($responseXML->Result); 72 if ($result_num > 0) { 73 $result = $responseXML->Result[0]; 74 array_push($returnList, $result->Status); 75 } 76 } 77 curl_multi_remove_handle($mh, $raised['handle']); 78 curl_close($raised['handle']); 79 } while ($remains); 80 //select前に全ての処理が終わっていたりすると 81 //複数の結果が入っていることがあるのでループが必要 82} while ($running); 83echo 'finished', PHP_EOL; 84curl_multi_close($mh);

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

よく理解していないので、的はずれな回答になるかもしれませんが。。。
curl_multi の接続上限設定
これ、参考になりませんか?

投稿2016/12/22 19:18

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yu-

2016/12/23 05:34

ご回答ありがとうございます。 ご紹介いただいた記事について参考にさせていただきます。
guest

0

自己解決

はっきりとした原因が分かった訳ではないのですが、

http://www.onlineaspect.com/2009/01/26/how-to-use-curl_multi-without-blocking/

を参考にコーディングして見たところ、期待通りの結果を得ることができました。

php

1function rolling_curl($urls, $callback, $custom_options = null) { 2 3 $rolling_window = 5; 4 $rolling_window = (sizeof($urls) &lt; $rolling_window) ? sizeof($urls) : $rolling_window; 5 6 $master = curl_multi_init(); 7 $curl_arr = array(); 8 9 $std_options = array(CURLOPT_RETURNTRANSFER =&gt; true, 10 CURLOPT_FOLLOWLOCATION =&gt; true, 11 CURLOPT_MAXREDIRS =&gt; 5); 12 $options = ($custom_options) ? ($std_options + $custom_options) : $std_options; 13 14 for ($i = 0; $i &lt; $rolling_window; $i++) { 15 $ch = curl_init(); 16 $options[CURLOPT_URL] = $urls[$i]; 17 curl_setopt_array($ch,$options); 18 curl_multi_add_handle($master, $ch); 19 } 20 21 do { 22 while(($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM); 23 if($execrun != CURLM_OK) 24 break; 25 while($done = curl_multi_info_read($master)) { 26 $info = curl_getinfo($done['handle']); 27 if ($info['http_code'] == 200) { 28 $output = curl_multi_getcontent($done['handle']); 29 30 $callback($output); 31 32 $ch = curl_init(); 33 $options[CURLOPT_URL] = $urls[$i++]; 34 curl_setopt_array($ch,$options); 35 curl_multi_add_handle($master, $ch); 36 37 curl_multi_remove_handle($master, $done['handle']); 38 } else { 39 // エラー発生時 40 } 41 } 42 } while ($running); 43 44 curl_multi_close($master); 45 return true; 46}

投稿2016/12/23 19:20

yu-

総合スコア12

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問