【独自】PHPタイムアウト処理総合まとめ
作成者: heiyeluren
ブログ: http://blog.csdn.net/heiyeshuwu
時間: 2012/8/8
【概要】
PHP の開発作業では、タイムアウト処理が使用される状況が数多くあります。いくつかのシナリオについて説明します。
1. 特定のバックエンド データ ソースの取得に失敗した場合、データはスキップされ、ページ全体の表示には影響しません。2. あるページの処理パフォーマンスの低下により Web サーバーが他のページにアクセスできなくなることを防ぐために、特定のページ動作設定が設定されます
3. 一部のアップロードや処理時間が不確実な状況では、プロセス全体のすべてのタイムアウトを無限に設定する必要があります。そうしないと、リンクの設定が不適切であると、原因不明の実行中断が発生します。
4. 複数のバックエンド モジュール (MySQL、Memcached、HTTP インターフェイス)。単一インターフェイスのパフォーマンスが低下しすぎて、フロント全体のデータ取得が遅くなり、ページを開く速度に影響を及ぼし、雪崩
5. 。 。タイムアウトが必要な場面は多々あります
これらはいずれもタイムアウトの設定を考慮する必要があるのですが、PHPにおけるタイムアウトはいくつかのカテゴリーに分かれており、それぞれ処理方法や戦略が異なりますので、仕組みを説明する目的でPHPでよく使われるタイムアウト処理をまとめてみました。 。
[Webサーバータイムアウト処理]
[ Apache ]
通常、パフォーマンスが非常に高い場合、デフォルトのタイムアウト設定は 30 秒ですが、ファイルをアップロードする場合やネットワーク速度が非常に遅い場合は、タイムアウト操作がトリガーされることがあります。
現在、Apache fastcgi php-fpm モードには 3 つのタイムアウト設定があります:
fastcgi タイムアウト設定:
次のように、httpd.conf の fastcgi 接続構成を変更します。
FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock
AddHandler php-fastcgi .php
|
|
IdleTimeout アイドル時間制限
MaxProcessCount 最大プロセス数 DefaultMinClassProcessCount 各プログラムによって開始されるプロセスの最小数 DefaultMaxClassProcessCount 各プログラムによって開始されるプロセスの最大数 IPCConnectTimeout プログラム応答タイムアウト IPCCommTimeout がプログラムと通信するための最大時間。この値の設定が小さすぎると、上記のエラーが発生する可能性があります。 MaxRequestsPerProcess は、各プロセスが完了できるプロセスの最大数です。制限に達すると自動的に終了します。
|
-------------------------------------------------- # 每次keep-alive 的最大请求数, 默认值是16 server.max-keep-alive-requests = 100
# keep-alive的最长等待时间, 单位是秒,默认值是5 server.max-keep-alive-idle = 1200
# lighttpd的work子进程数,默认值是0,单进程运行 server.max-worker = 2
# 限制用户在发送请求的过程中,最大的中间停顿时间(单位是秒), # 如果用户在发送请求的过程中(没发完请求),中间停顿的时间太长,lighttpd会主动断开连接 # 默认值是60(秒) server.max-read-idle = 1200
# 限制用户在接收应答的过程中,最大的中间停顿时间(单位是秒), # 如果用户在接收应答的过程中(没接完),中间停顿的时间太长,lighttpd会主动断开连接 # 默认值是360(秒) server.max-write-idle = 12000
# 读客户端请求的超时限制,单位是秒, 配为0表示不作限制 # 设置小于max-read-idle时,read-timeout生效 server.read-timeout = 0
# 写应答页面给客户端的超时限制,单位是秒,配为0表示不作限制 # 设置小于max-write-idle时,write-timeout生效 server.write-timeout = 0
# 请求的处理时间上限,如果用了mod_proxy_core,那就是和后端的交互时间限制, 单位是秒 server.max-connection-idle = 1200 -------------------------------------------------- |
----------------------------------------------- --- --- # 1 回あたりのキープアライブ リクエストの最大数、デフォルト値は 16 です server.max-keep-alive-requests = 100 # キープアライブの最大待機時間、単位は秒、デフォルト値は 5 です server.max-keep-alive-idle = 1200 # lighttpd の作業サブプロセスの数、デフォルト値は 0、単一プロセス操作 server.max-worker = 2 # ユーザーがリクエストを送信するときの最大一時停止時間 (秒単位) を制限します。 # ユーザーがリクエストの送信中に (リクエストを完了せずに) 長時間停止した場合、lighttpd は積極的に切断します # デフォルト値は 60 (秒) です server.max-read-idle = 1200 # ユーザーが応答を受信するまでの最大一時停止時間 (秒単位) を制限します。 # 応答を受信している間 (完了していない) にユーザーが長時間停止した場合、lighttpd は積極的に切断します # デフォルト値は 360 (秒) です server.max-write-idle = 12000 # クライアント要求の読み取りのタイムアウト制限。単位は秒で、制限がないことを示すには 0 に設定されます # 設定が max-read-idle 未満の場合、read-timeout が有効になります server.read-timeout = 0 # クライアントに応答ページを書き込むためのタイムアウト制限。単位は秒で、制限がないことを示すには 0 に設定されます # 設定が max-write-idle 未満の場合、write-timeout が有効になります server.write-timeout = 0 # リクエスト処理時間の上限。mod_proxy_core を使用する場合、単位は秒です。 server.max-connection-idle = 1200 ----------------------------------------------- --- --- |
#Fastcgi: (バックエンド fastcgi に有効、fastcgi はプロキシ モードに属しません) fastcgi_connect_timeout 5; #接続タイムアウト fastcgi_send_timeout 10; #書き込みタイムアウト fastcgi_read_timeout 10; #読み取りタイムアウト
#Proxy: (プロキシ/アップストリームに有効) proxy_connect_timeout 15 秒; #接続タイムアウト proxy_read_timeout 24 秒; #読み取りタイムアウト proxy_send_timeout 10 秒; #書き込みタイムアウト } |
//... Sets the limit on the number of simultaneous requests that will be served. Equivalent to Apache MaxClients directive. Equivalent to PHP_FCGI_CHILDREN environment in original php.fcgi Used with any pm_style. #php-cgi的进程数量
The timeout (in seconds) for serving a single request after which the worker process will be terminated Should be used when 'max_execution_time' ini option does not stop script execution for some reason '0s' means 'off' #php-fpm 请求执行超时时间,0s为永不超时,否则设置一个 Ns 为超时的秒数
The timeout (in seconds) for serving of single request after which a php backtrace will be dumped to slow.log file '0s' means 'off'
|
//...
処理される同時リクエストの数の制限を設定します。
Apache MaxClients ディレクティブと同等です。
元の php.fcgi の PHP_FCGI_CHILDREN 環境に相当します
任意の pm_style と一緒に使用されます。
#php-cgi プロセス数
<値の名前="max_children">128値>
ワーカー プロセスが終了するまでの単一リクエストの処理のタイムアウト (秒単位)
'max_execution_time' ini オプションが何らかの理由でスクリプトの実行を停止しない場合に使用する必要があります
「0」は「オフ」を意味します
#php-fpm リクエスト実行タイムアウト、0s はタイムアウトしないことを意味します。それ以外の場合は、タイムアウトの秒数として Ns を設定します
|
その他のヒント:
1. エクスペリエンスの概要によると、cURL バージョン >= libcurl/7.21.0 バージョンでは、ミリ秒のタイムアウトが確実に有効になることを覚えておいてください。
2.curl_multi のミリ秒タイムアウトにも問題があります。 。単一アクセスはミリ秒レベルのタイムアウトをサポートしており、複数の呼び出しが並行して行われる場合、curl_multi は不正確になります
[ストリーミング経由で HTTP にアクセス]
HTTP プロトコルの処理には、curl 以外にも fsockopen やファイル操作関数を使用することが多いため、タイムアウト処理も必要です。
一般的な接続タイムアウトは直接設定できますが、ストリーム読み取りタイムアウトは個別に処理する必要があります。
これを処理する独自のコードを作成します:
$tmCurrent = gettimeofday();
$intUSGone = ($tmCurrent['sec'] - $tmStart['sec']) * 1000000
+ ($tmCurrent['usec'] - $tmStart['usec']);
if ($intUSGone > $this->_intReadTimeoutUS) {
false を返します;
}
または、組み込みのストリーム処理関数 stream_set_timeout() および stream_get_meta_data() を使用して次の処理を行います。
// Timeout in seconds
$timeout = 5; $fp = fsockopen("example.com", 80, $errno, $errstr, $timeout); if ($fp) { fwrite($fp, "GET / HTTP/1.0rn"); fwrite($fp, "Host: example.comrn"); fwrite($fp, "Connection: Closernrn"); stream_set_blocking($fp, true); //重要,设置为非阻塞模式 stream_set_timeout($fp,$timeout); //设置超时 $info = stream_get_meta_data($fp); while ((!feof($fp)) && (!$info['timed_out'])) { $data .= fgets($fp, 4096); $info = stream_get_meta_data($fp); ob_flush; flush(); } if ($info['timed_out']) { echo "Connection Timed Out!"; } else { echo $data; } } |
$タイムアウト = 5
$fp = fsockopen("example.com", 80, $errno, $errstr, $timeout);
if ($fp) { fwrite($fp, "GET / HTTP/1.0rn");
fwrite($fp, "ホスト: example.comrn");fwrite($fp, "接続: Closernrn");
Stream_set_blocking($fp, true) //重要、非ブロッキング モードに設定します$timeout = array(
'http' => array( 'timeout' => 5 //设置一个超时时间,单位为秒 ) ); $ctx = stream_context_create($timeout); $text = file_get_contents("http://example.com/", 0, $ctx); ?> |
$timeout = array(<🎜> <🎜> 'http' => 'timeout' => 5 //タイムアウトを秒単位で設定します ) ); $ctx = stream_context_create($timeout); $text = file_get_contents("http://example.com/", 0, $ctx); ?> |
$timeout = array(
'http' => array( 'timeout' => 5 //设置一个超时时间,单位为秒 ) ); $ctx = stream_context_create($timeout); if ($fp = fopen("http://example.com/", "r", false, $ctx)) { while( $c = fread($fp, 8192)) { echo $c; } fclose($fp); } ?> |
'http' => 'timeout' => 5 //タイムアウトを秒単位で設定します
)
); $ctx = stream_context_create($timeout);
if ($fp = fopen("http://example.com/", "r", false,
$ctx
while( $c = fread($fp, 8192)) {
echo $c;
}
fclose($fp);}
?>テーブル>
【MySQL】
php の mysql クライアントには、mysqli にも mysql にもタイムアウト オプションがありません。libmysql にはタイムアウト オプションがありますが、php ではそれを隠しています。
PHP でこの操作を使用するには、いくつかの MySQL 操作定数を自分で定義する必要があります。関係する主な定数は次のとおりです。
MYSQL_OPT_READ_TIMEOUT=11;//自己定义读写超时常量
if (!defined('MYSQL_OPT_READ_TIMEOUT')) { define('MYSQL_OPT_READ_TIMEOUT', 11); } if (!defined('MYSQL_OPT_WRITE_TIMEOUT')) { define('MYSQL_OPT_WRITE_TIMEOUT', 12); } //设置超时 $mysqli = mysqli_init(); $mysqli->options(MYSQL_OPT_READ_TIMEOUT, 3);
$mysqli->options(MYSQL_OPT_WRITE_TIMEOUT, 1); //连接数据库 $mysqli->real_connect("localhost", "root", "root", "test"); if (mysqli_connect_errno()) { printf("Connect failed: %s/n", mysqli_connect_error()); exit(); } //执行查询 sleep 1秒不超时 printf("Host information: %s/n", $mysqli->host_info); if (!($res=$mysqli->query('select sleep(1)'))) { echo "query1 error: ". $mysqli->error ."/n"; } else { echo "Query1: query success/n"; } //执行查询 sleep 9秒会超时 if (!($res=$mysqli->query('select sleep(9)'))) { echo "query2 error: ". $mysqli->error ."/n"; } else { echo "Query2: query success/n"; } $mysqli->close(); echo "close mysql connection/n"; ?> |
//独自の読み取りおよび書き込みタイムアウト定数を定義します<🎜> <🎜>if (!define('MYSQL_OPT_READ_TIMEOUT')) {<🎜> <🎜><🎜>define('MYSQL_OPT_READ_TIMEOUT', 11);<🎜><🎜> <🎜>}<🎜> <🎜>if (!define('MYSQL_OPT_WRITE_TIMEOUT')) {<🎜> <🎜><🎜>define('MYSQL_OPT_WRITE_TIMEOUT', 12);<🎜><🎜> <🎜>}<🎜> <🎜>//タイムアウトを設定します<🎜> <🎜>$mysqli = mysqli_init();<🎜> <🎜><🎜>$mysqli->options(MYSQL_OPT_READ_TIMEOUT, 3);<🎜><🎜> <🎜><🎜>$mysqli->options(MYSQL_OPT_WRITE_TIMEOUT, 1);<🎜><🎜> <🎜> <🎜> <🎜>//データベースに接続します<🎜> <🎜>$mysqli->real_connect("localhost", "root", "root", "test");<🎜> <🎜>if (mysqli_connect_errno()) {<🎜> <🎜> printf("接続に失敗しました: %s/n", mysqli_connect_error());<🎜> <🎜> exit();<🎜> <🎜>}<🎜> <🎜>//タイムアウトなしでクエリスリープを 1 秒間実行します<🎜> <🎜>printf("ホスト情報: %s/n", $mysqli->host_info);<🎜> <🎜>if (!($res=$mysqli->query('select sleep(1)'))) {<🎜> <🎜> echo "query1 エラー: ". $mysqli->error ."/n";<🎜> <🎜>} else {<🎜> <🎜> echo "Query1: クエリ成功/n";<🎜> <🎜>}<🎜> <🎜>//クエリスリープの実行は 9 秒でタイムアウトします<🎜> <🎜>if (!($res=$mysqli->query('select sleep(9)'))) {<🎜> <🎜> echo "query2 エラー: ". $mysqli->error ."/n";<🎜> <🎜>} else {<🎜> <🎜> echo "Query2: クエリ成功/n";<🎜> <🎜>}<🎜> <🎜>$mysqli->close();<🎜> <🎜>echo "mysql 接続を閉じる/n";<🎜> <🎜>?><🎜> |
// 接続タイムアウトを作成します (Memcached に接続します) memcached_st* MemCacheProxy::_create_handle() { memcached_st * mmc = NULL; memcached_return_t prc; if (_mpool != NULL) { // プールから取得 mmc = memcached_pool_pop(_mpool, false, &prc); if (mmc == NULL) { __LOG_WARNING__("MemCacheProxy", "プール エラー [%d] からハンドルを取得", (int)prc); } mmc を返します; }
memcached_st* ハンドル = memcached_create(NULL); if (ハンドル == NULL){ __LOG_WARNING__("MemCacheProxy", "create_handle エラー"); NULL を返す; }
// 接続/読み取りタイムアウトを設定 memcached_behavior_set(ハンドル、MEMCACHED_BEHAVIOR_HASH、MEMCACHED_HASH_DEFAULT); memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_NO_BLOCK, _noblock); //タイムアウト設定を有効にするには、パラメータ MEMCACHED_BEHAVIOR_NO_BLOCK が 1 に設定されます。重要なときに悲劇的なことが起こり、簡単に雪崩を引き起こす可能性があります memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, _connect_timeout); //接続タイムアウト memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, _read_timeout) // 読み取りタイムアウト memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_SND_TIMEOUT, _send_timeout) // 書き込みタイムアウト memcached_behavior_set(ハンドル、MEMCACHED_BEHAVIOR_POLL_TIMEOUT、_poll_timeout);
// 一貫したハッシュを設定します // memcached_behavior_set_distribution(ハンドル, MEMCACHED_DISTRIBUTION_CONSISTENT); memcached_behavior_set(ハンドル、MEMCACHED_BEHAVIOR_DISTRIBUTION、MEMCACHED_DISTRIBUTION_CONSISTENT);
memcached_return rc; for (uint i = 0; i < _server_count; i++){ rc = memcached_server_add(handle, _ips[i], _ports[i]); if (MEMCACHED_SUCCESS != rc) { __LOG_WARNING__("MemCacheProxy", "サーバーの追加 [%s:%d] に失敗しました。", _ips[i], _ports[i]); } }
_mpool = memcached_pool_create(handle, _min_connect, _max_connect); if (_mpool == NULL){ __LOG_WARNING__("MemCacheProxy", "create_pool error"); return NULL; }
mmc = memcached_pool_pop(_mpool, false, &prc); if (mmc == NULL) { __LOG_WARNING__("MyMemCacheProxy", "プールからのハンドルの取得エラー [%d]", (int)prc); } //__LOG_DEBUG__("MemCacheProxy", "get handle [%p]", handle); mmc を返す; }
//1 つのキーを超時設定します(memcached に 1 つのデータを設定します) bool MemCacheProxy::_add(memcached_st* ハンドル, unsigned int* key, const char* value, int len, unsigned int timeout) { memcached_return rc;
char tmp[1024]; snprintf(tmp, sizeof (tmp), "%u#%u", key[0], key[1]); //タイムアウトあり rc = memcached_set(handle, tmp, strlen(tmp), (char*)value, len, timeout, 0); if (MEMCACHED_SUCCESS != rc){ return false; } true を返す; } |
//Memcache 読み取りデータのタイムアウト (未設定)
libmemcahed ソース コードのインターフェイス定義:
LIBMEMCACHED_API char *memcached_get(memcached_st *ptr,const char *key, size_t key_length,size_t *value_length,uint32_t *flags,memcached_return_t *error);
LIBMEMCACHED_API memcached_return_t memcached_mget(memcached_st *ptr,const char * const *keys,const size_t *key_length,size_tnumber_of_keys);
インターフェースを見ると、データ読み取り時にタイムアウト設定がないことがわかります。
詳細な読書:
http://hi.baidu.com/chinauser/item/b30af90b23335dde73e67608
http://libmemcached.org/libMemcached.html
【タイムアウトの実装方法】
プログラムにはタイムアウト機能が必要です。たとえば、バックエンドの Socket モジュールに単独でアクセスし、その Socket モジュールが上記のいずれにも属していない場合、そのプロトコルもプライベートであるため、現時点では、タイムアウト処理戦略を実装するには、いくつかの処理コードが必要になる場合があります。
[PHP でのタイムアウトの実装]
1. 初級: 最も単純なタイムアウト実装 (第 2 レベルのタイムアウト)
考え方は簡単です。バックエンドに接続し、接続がない場合はループを続けて、現在の時刻とタイムアウト時刻の差を確認します。
元のタイムアウトは php ソケットに実装されています: (現在の時刻は各ループで減分され、パフォーマンスが非常に低下し、CPU 使用率が高くなります)
2. アップグレード: PHP 独自の非同期 IO を使用して実装します (ミリ秒タイムアウト)
説明: ?
socket_select() を使用してタイムアウトを実装します socket_select(..., Floor($timeout), ceil($timeout*1000000));
socket_select() のタイムアウト コードを使用します (理解するには非同期 IO プログラミングの知識が必要です)
注: タイムアウトの実装に関しては、PHP、Python、Perl などのスクリプト言語に関係なく、基本的な最下層は C&C++ の実装メソッドです。これらのタイムアウト処理を理解する必要があり、Linux プログラミングの知識も必要です。そしてネットワークプログラミング。
詳細な読書: http://blog.sina.com.cn/s/blog_4462f8560100tvgo.html http://blog.csdn.net/thimin/article/details/1530839 http://hi.baidu.com/xjtdy888/item/93d9daefcc1d31d1ea34c992 http://blog.csdn.net/byxdaz/article/details/5461142 http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520112163171778/ http://hi.baidu.com/suyupin/item/df10004decb620e91f19bcf5 http://stackoverflow.com/questions/7092633/connect-timeout-with-alarm http://stackoverflow.com/questions/7089128/linux-tcp-connect-with-select-fails-at-testserver?lq=1 http://cppentry.com/bencandy.php?fid=54&id=1129
【概要】
1. PHP アプリケーション層でタイムアウトを設定するには? PHP にはさまざまなレベルの処理タイムアウトがあります。レベルに応じて、フロントエンドがバックエンド タイムアウトに対応する必要があります。 ブラウザ (クライアント) -> アクセス層 -> PHP -> バックエンド (MySQL、Memcached) つまり、アクセス層(Webサーバー層)のタイムアウト時間は、PHP(PHP-FPM)で設定したタイムアウト時間より長くなければなりません。そうしないと、処理が完了する前にタイムアウトして終了します。正面は大惨事になるだろう。また、PHP のタイムアウトは、バックエンド (MySQL、HTTP、Memcached) にアクセスする PHP 自体のタイムアウトより大きくする必要があります。そうでない場合、結果は前と同じになります。
2. タイムアウト設定の原則は何ですか? タイムアウトしないことを望むコード (アップロードや定期的に実行されるプログラムなど) の場合でも、主に PHP プロセスが永続的に実行されないようにするために、12 時間などのタイムアウトを設定することをお勧めします。縛られている、またはバックエンドが他のページにサービスを提供できず、最終的にすべてのマシンに雪崩を引き起こします。迅速な応答が必要なプログラムの場合は、バックエンドのタイムアウトを、接続で 500 ミリ秒、読み取りで 1 秒、書き込みで 1 秒など、短く設定することをお勧めします。この速度により、アプリケーションの雪崩や書き込みの問題が大幅に軽減されます。サーバーに過負荷がかかることはありません。
3. タイムアウト アクセスを独自に開発するのは適切ですか? 一般に、それが最後の手段ではない場合は、多くの既存のネットワーク プログラミング フレームワークまたは基本ライブラリを使用することをお勧めします。これらには、通常、一部のネットワーク IO ライブラリなどのタイムアウト実装が含まれており、それらの組み込みのものを使用してみてください。ホイールはバグが発生しやすく、メンテナンスが不便です (ただし、学習目的の場合は別の問題です)。
4. その他の提案 タイムアウトはすべてのアプリケーションにとって大きな問題であり、アプリケーションを開発する際には考慮する必要があります。いくつかのアプリケーションでタイムアウトが数百秒に設定されているのを見たことがあります。例を示します。たとえば、php-fpm が 128 個の php-cgi プロセスを開いていて、タイムアウトが 32 秒に設定されている場合、バックエンド サービスが不十分な場合、極端な場合、応答できるリクエストの最大数は制限されます。 to/秒は:
128 / 32 = 4 お読みのとおり、1 秒間に 4 つのリクエストしか処理できないため、サービスが悪すぎます。 php-cgi プロセスのサイズを増やすことはできますが、メモリ使用量やプロセス間のスイッチングコストも増加し、サービスが不安定になります。したがって、適切なタイムアウト値を設定するか、バックエンドのパフォーマンスを向上させるようにしてください。
この記事には経験値や参考となる内容が含まれておりますので、不備がございましたらご指摘ください。
|