【 概述 】
在PHP开发中工作里非常多使用到超时处理到超时的场合,我说几个场景:
1. 异步获取数据如果某个后端数据源获取不成功则跳过,不影响整个页面展现
2. 为了保证Web服务器不会因为当个页面处理性能差而导致无法访问其他页面,则会对某些页面操作设置
3. 对于某些上传或者不确定处理时间的场合,则需要对整个流程中所有超时设置为无限,否则任何一个环节设置不当,都会导致莫名执行中断
4. 多个后端模块(MySQL、Memcached、HTTP接口),为了防止单个接口性能太差,导致整个前面获取数据太缓慢,影响页面打开速度,引起雪崩
5. 很多需要超时的场合
这些地方都需要考虑超时的设定,但是PHP中的超时都是分门别类,各个处理方式和策略都不同,为了系统的描述,我总结了PHP中常用的超时处理的总结。
【Web服务器超时处理】
[ Apache ]
一般在性能很高的情况下,缺省所有超时配置都是30秒,但是在上传文件,或者网络速度很慢的情况下,那么可能触发超时操作。
目前 apache fastcgi php-fpm 模式 下有三个超时设置:
fastcgi 超时设置:
修改 httpd.conf 的fastcgi连接配置,类似如下:
FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock
ScriptAlias /fcgi-bin/ "/home/forum/apache/apache_php/cgi-bin/"
AddHandler php-fastcgi .php
Action php-fastcgi /fcgi-bin/php-cgi
AddType application/x-httpd-php .php
缺省配置是 30s,如果需要定制自己的配置,需要修改配置,比如修改为100秒:(修改后重启 apache):
<IfModule mod_fastcgi.c>
FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock -idle-timeout 100
ScriptAlias /fcgi-bin/ "/home/forum/apache/apache_php/cgi-bin/"
AddHandler php-fastcgi .php
Action php-fastcgi /fcgi-bin/php-cgi
AddType application/x-httpd-php .php
</IfModule>
如果超时会返回500错误,断开跟后端php服务的连接,同时记录一条apache错误日志:
1
2
[Thu Jan 27 18:30:15 2011] [error] [client 10.81.41.110] FastCGI: comm with server "/home/forum/apache/apache_php/cgi-bin/php-cgi" aborted: idle timeout (30 sec)
[Thu Jan 27 18:30:15 2011] [error] [client 10.81.41.110] FastCGI: incomplete headers (0 bytes) received from server "/home/forum/apache/apache_php/cgi-bin/php-cgi"
其他 fastcgi 配置参数说明:
IdleTimeout 发呆时限
ProcessLifeTime 一个进程的最长生命周期,过期之后无条件kill
MaxProcessCount 最大进程个数
DefaultMinClassProcessCount 每个程序启动的最小进程个数
DefaultMaxClassProcessCount 每个程序启动的最大进程个数
IPCConnectTimeout 程序响应超时时间
IPCCommTimeout 与程序通讯的最长时间,上面的错误有可能就是这个值设置过小造成的
MaxRequestsPerProcess 每个进程最多完成处理个数,达成后自杀
[ Lighttpd ]
配置:lighttpd.conf
Lighttpd配置中,关于超时的参数有如下几个(篇幅考虑,只写读超时,写超时参数同理):
主要涉及选项:
server.max-keep-alive-idle = 5
server.max-read-idle = 60
server.read-timeout = 0
server.max-connection-idle = 360
--------------------------------------------------
# 每次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 が有効になります
サーバー.読み取りタイムアウト = 0
# クライアントに応答ページを書き込むためのタイムアウト制限。単位は秒で、制限がないことを示すには 0 に設定します
# 設定が max-write-idle 未満の場合、write-timeout が有効になります
server.write-timeout = 0
# リクエスト処理時間の上限は、mod_proxy_coreを使用する場合、単位は秒です。
サーバー.最大接続アイドル = 1200
--------------------------------------------------
説明:
キープアライブ接続での連続したリクエストの場合、最初のリクエスト コンテンツを送信する最大間隔はパラメータ max-read-idle によって決まります。2 番目以降のリクエスト コンテンツを送信する最大間隔はパラメータ max によって決まります。 -keep-alive- アイドル決定。リクエスト間のタイムアウトも max-keep-alive-idle によって決まります。リクエストコンテンツの送信の合計タイムアウトは、パラメータ read-timeout によって決まります。 Lighttpd がバックエンドと対話するためのタイムアウトは、max-connection-idle によって決まります。
さらに読む:
http://www.snooda.com/read/244
[ Nginx ]
設定: nginx.conf
http {
#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 秒 #書き込みタイムアウト
}
説明:
Nginx のタイムアウト設定は非常に明確で理解しやすいです。上記のタイムアウトはさまざまな動作モードに対応していますが、タイムアウトによって引き起こされる問題は数多くあります。
さらに読む:
http://hi.baidu.com/pibuchou/blog/item/a1e330dd71fb8a5995ee3753.html
http://hi.baidu.com/pibuchou/blog/item/7cbccff0a3b77dc60b46e024.html
http://hi.baidu.com/pibuchou/blog/item/10a549818f7e4c9df703a626.html
http://www.apoyl.com/?p=466
【PHP自体のタイムアウト処理】
[PHP-fpm]
設定: php-fpm.conf
//...
処理される同時リクエストの数の制限を設定します。
Apache MaxClients ディレクティブと同等です。
元の php.fcgi の PHP_FCGI_CHILDREN 環境に相当します
任意の pm_style と一緒に使用されます。
#php-cgi プロセス数
<値名="max_children">128値>
単一のリクエストを処理するためのタイムアウト (秒単位)。その後ワーカー プロセスが終了します
「max_execution_time」ini オプションが何らかの理由でスクリプトの実行を停止しない場合に使用する必要があります
「0」は「オフ」を意味します
#php-fpm リクエスト実行タイムアウト。0 はタイムアウトしないことを意味します。それ以外の場合は、タイムアウトの秒数として N を設定します
単一のリクエストを処理するためのタイムアウト (秒単位)。その後、php バックトレースがslow.log ファイルにダンプされます
「0」は「オフ」を意味します
構成>
説明:
php.ini には PHP スクリプトの最大実行時間を設定できるパラメータ max_execution_time がありますが、php-cgi (php-fpm) ではこのパラメータは有効になりません。 PHP スクリプトの最大実行時間を実際に制御できます:
1
つまり、max_execution_time を mod_php5.so モードで実行すると有効になりますが、php-fpm モードで実行すると有効になりません。
詳しい読み方:
http://blog.s135.com/file_get_contents/
[PHP]
設定: php.ini
オプション:
最大実行時間 = 30
またはコードで設定します:
ini_set(“最大実行時間”, 30);
set_time_limit(30);
説明:
たとえば、0 を設定するとタイムアウトになることはありませんが、PHP のセーフモードがオンになっている場合、これらの設定は有効になりません。
効果は同じですが、具体的な内容については php-fpm の部分を参照する必要があります。php-fpm で request_terminate_timeout が設定されている場合、max_execution_time は有効になりません。
[バックエンドとインターフェースのアクセスタイムアウト]
【HTTPアクセス】
一般に、私たちはさまざまな方法で HTTP にアクセスします。主に、curl、socket、file_get_contents() およびその他のメソッドです。
相手のサーバーが応答しない場合は、サーバー全体が簡単に停止してしまうため、http にアクセスする際のタイムアウトの問題も考慮する必要があります。
【CURLアクセスHTTP】
CURL は、HTTP プロトコル インターフェイスにアクセスするために一般的に使用される信頼性の高い lib ライブラリであり、高いパフォーマンスといくつかの同時実行サポート機能を備えています。
カール:
curl_setopt($ch, opt) は、主に以下を含むいくつかのタイムアウト設定を設定できます:
*(重要) CURLOPT_TIMEOUT は、cURL の実行が許可される最大秒数を設定します。
*(重要) CURLOPT_TIMEOUT_MS は、cURL が実行できる最大ミリ秒数を設定します。 (cURL 7.16.2 で追加されました。PHP 5.2.3 以降で利用可能です。)
CURLOPT_CONNECTTIMEOUT 接続を開始するまでの待機時間。0 に設定すると、無期限に待機します。
CURLOPT_CONNECTTIMEOUT_MS 接続試行を待機する時間 (ミリ秒)。 0 に設定すると、無限に待機します。 cURL 7.16.2 で追加されました。 PHP 5.2.3 以降で利用可能です。
CURLOPT_DNS_CACHE_TIMEOUT は、DNS 情報をメモリに保存する時間を設定します。デフォルトは 120 秒です。
Curl の通常の第 2 レベルのタイムアウト:
$ch =curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 60) //秒数を設定するだけです
;
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_USERAGENT, $define_vars['HTTP_USER_AGENT']);
Curl の通常の第 2 レベルのタイムアウトの使用:
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
Curl がミリ秒のタイムアウトを実行する必要がある場合は、値を増やす必要があります:
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
または:
curl_setopt ($ch, CURLOPT_NOSIGNAL, true); ミリ秒レベルのタイムアウト設定をサポートできます
ミリ秒タイムアウトのカールの例:
if (!isset($_GET['foo'])) {
// クライアント
$ch =curl_init('http://example.com/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_NOSIGNAL, 1); curl_setopt($ch, CURLOPT_NOSIGNAL, 1); // これはミリ秒のタイムアウトに設定する必要があることに注意してください
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200); //ミリ秒単位のタイムアウト、cURL 7.16.2 で追加。 PHP 5.2.3 以降で利用可能
$data =curl_exec($ch);
$curl_errno =curl_errno($ch);
$curl_error =curl_error($ch);
curl_close($ch);
if ($curl_errno > 0) {
echo "cURL エラー ($curl_errno): $curl_errorn";
} else {
echo "受信したデータ: $datan";
}
} その他 {
// サーバー
睡眠(10);
echo "完了。";
}
?>
その他のヒント:
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() を使用して以下を処理します。
// 秒単位でタイムアウト
$タイムアウト = 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); //重要、ノンブロックモードに設定します
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;
フラッシュ();
}
if ($info['timed_out']) {
echo "接続がタイムアウトしました!";
} その他 {
$data をエコー;
}
}
file_get_contents 超時間:
$timeout = 配列(
'http' =>配列(
'タイムアウト' => 5 //超過時間を設定します。単位は秒です
)
);
$ctx = stream_context_create($timeout);
$text = file_get_contents("http://example.com/", 0, $ctx);
?>
fopen超時間:
$timeout = 配列(
'http' =>配列(
'タイムアウト' => 5 //超過時間を設定します。単位は秒です
)
);
$ctx = stream_context_create($timeout);
if ($fp = fopen("http://example.com/", "r", false, $ctx)) {
while( $c = fread($fp, 8192)) {
エコー $c;
}
fclose($fp);
}
?>
【MySQL】
php 内の mysql ゲスト エンドには、mysqli と mysql の両方にタイム タイム オプションが設定されていませんが、php で実行済みである場合に限り、libmysql にはタイム タイム オプションが提供されています。
PHP でこの操作捏造をどのように使用するか、いくつかの MySQL 操作常量を自己決定する必要があります。主に関係する常量は次のとおりです。
MYSQL_OPT_READ_TIMEOUT=11;
MYSQL_OPT_WRITE_TIMEOUT=12;
これら 2 つは、後でオプションを使用して対応する値を設定できます。
不过有注意点,mysql 内部实现:
1. 超時間配置単位は秒、最小配置は 1 秒
2. ただし、mysql の最下層の読み取りセッションは 2 回繰り返されるため、実際の読み取り時間は 3 秒です
2 回の繰り返し + 1 回目の繰り返し = 3 倍の時間です。つまり、最小時間は 3 秒であり、この値を下回ることはなく、大部分の用途では受け入れられますが、一部の用途では改善が必要です。
mysql の設定を監視する php の例:
//自己定义读写超時常量
if (!define('MYSQL_OPT_READ_TIMEOUT')) {
定義('MYSQL_OPT_READ_TIMEOUT', 11);
}
if (!define('MYSQL_OPT_WRITE_TIMEOUT')) {
定義('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 "クエリ 1 エラー: "。 $mysqli->エラー ."/n";
} その他 {
echo "Query1: クエリ成功/n";
}
//クエリを実行すると、9秒後にスリープがタイムアウトします
if (!($res=$mysqli->query('select sleep(9)'))) {
echo "query2 エラー: ". $mysqli->error ."/n";
} その他 {
echo "Query2: クエリ成功/n";
}
$mysqli->close();
echo "mysql 接続を閉じる/n";
?>
詳しい読み方:
http://blog.csdn.net/heiyeshuwu/article/details/5869813
【Memcached】
【PHP拡張機能】
php_memcache クライアント:
接続タイムアウト: bool Memcache::connect ( string $host [, int $port [, int $timeout ]] )
取得および設定する場合、明確なタイムアウト設定パラメータはありません。
libmemcached クライアント: PHP インターフェイスには明らかなタイムアウト パラメーターがありません。
注: したがって、PHP で Memcached にアクセスする場合は、いくつかの操作を自分でハックするか、オンライン パッチを参照する必要があるため、多くの問題が発生します。
[C&C++ アクセス Memcached]
クライアント: libmemcached クライアント
注: memcache のタイムアウト構成は、より小さく構成できます。たとえば、この時間を超える場合は、データベースからクエリを実行することをお勧めします。
以下は、セットデータの接続と読み取りのタイムアウトの C++ の例です:
//接続タイムアウトを作成します (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
rc = memcached_server_add(ハンドル, _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 エラー");
NULL を返します;
}
mmc = memcached_pool_pop(_mpool, false, &prc);
if (mmc == NULL) {
__LOG_WARNING__("MyMemCacheProxy", "プール エラー [%d] からハンドルを取得", (int)prc);
}
//__LOG_DEBUG__("MemCacheProxy", "ハンドルを取得 [%p]", ハンドル);
mmc を返します;
}
//1 つのキーを超時設定します (memcached に 1 つのデータを設定します)
bool MemCacheProxy::_add(memcached_st* ハンドル、unsigned int* キー、const char* 値、int len、unsigned int タイムアウト)
{
memcached_return rc;
char tmp[1024];
snprintf(tmp, sizeof (tmp), "%u#%u", key[0], key[1]);
//タイムアウトあり
rc = memcached_set(ハンドル, tmp, strlen(tmp), (char*)value, len, タイムアウト, 0);
if (MEMCACHED_SUCCESS != rc){
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
【どうやって实现超時間】
プログラム中にこのような機能が必要になる場合があります。たとえば、後端のソケット モジュールを個別に接続する場合、ソケット モジュールが上で説明したものに属さない場合、その指示も私のものです。処理ポリシーには、この時点でいくつかの処理コードが必要です。
[PHP中超時間实现]
一、初级:最简单的超時間实现(秒超時間)
考え抜かれた例: 後端に接続し、その後非ブロックモードに設定し、接続されていない場合は循環し、現在時間と超過時間の差を判断します。
php ソケット中实现原始的超過時間:(每次循環环都当前時間间去减,性能会很差,cpu占用会较高)
$host = "127.0.0.1";
$port = "80";
$タイムアウト = 15; //秒単位でタイムアウト
$socket =socket_create(AF_INET, SOCK_STREAM, SOL_TCP)
または die("ソケットを作成できません");
socket_set_nonblock($socket) //遮断モードに設定する必要があります
または die("ソケットn に非ブロックを設定できません");
$time = time();
//循環する季節每次都减去相应值
while (!@socket_connect($socket, $host, $port)) //如果没有连接上就一直死循環
{
$err =socket_last_error($socket);
if ($err == 115 || $err == 114)
{
if ((time() - $time) >= $timeout) //每次都必要去判断一下否か超時了
{
ソケットクローズ($ソケット);
die("接続がタイムアウトしました。n");
}
睡眠(1);
続けます;
}
die(socket_strerror($err) . "n");
}
Socket_set_block($this->socket) //ブロッキングモードを復元します
または die("ソケットn にブロックを設定できません");
?>
2. アップグレード: PHP 独自の非同期 IO を使用して実装します (ミリ秒タイムアウト)
説明:
非同期 IO: 非同期 IO の概念は、同期 IO に関連しています。非同期プロシージャ呼び出しが発行された場合、呼び出し元は結果をすぐには取得しません。実際に通話を処理するコンポーネントは、通話が完了するとステータス、通知、コールバックを通じて発信者に通知します。非同期 IO は、ビットを小さなグループ (8 ビット、1 文字、またはそれ以上) に転送します。送信者はいつでもこれらのビットのグループを送信できますが、受信者はそれらがいつ到着するかわかりません。
多重化: 多重化モデルは複数の IO 操作を検出し、操作できるように操作可能なコレクションを返します。これにより、ブロッキング IO が各 IO をいつでも処理できないという判断と、システム リソースの非ブロッキング占有が回避されます。
タイムアウトを実装するには、socket_select() を使用します
ソケット_select(…, フロア($timeout), ceil($timeout*1000000));
select の特徴: タイムアウトをマイクロ秒レベルで設定可能!
socket_select() のタイムアウト コードを使用します (理解するには、非同期 IO プログラミングの知識が必要です)
### クラスの呼び出し ####
$server = 新しいサーバー;
$client = 新しいクライアント;
(;;) {
foreach ($select->can_read(0) as $socket) {
If ($socket == $client->socket) {
// 新しいクライアント ソケット
$select->add(socket_accept($client->socket));
}
他に{
// $socket に読むべきものがある
}
}
}
?>
### 非同期多重IO&タイムアウト接続処理クラス ###
クラス選択{
var $sockets;
関数選択($sockets) {
$this->sockets = array();
foreach ($sockets として $socket) {
$this->add($socket);
}
}
関数 add($add_socket) {
Array_push($this->ソケット,$add_socket);
}
関数削除($remove_socket) {
$sockets = array();
foreach ($this->ソケットを $socket として) {
If($remove_socket != $socket)
$sockets[] = $socket;
}
$this->ソケット = $ソケット;
}
関数 can_read($timeout) {
$read = $this->ソケット;
ソケット選択($read,$write = NULL,$excel = NULL,$timeout);
$read を返します;
}
関数 can_write($timeout) {
$write = $this->ソケット;
ソケット選択($read = NULL,$write,$excel = NULL,$timeout);
$write を返します;
}
} ?>
[C&C++ でのタイムアウトの実装]
一般に、Linux C/C++ では、alarm() を使用してタイマーを設定して第 2 レベルのタイムアウトを達成したり、select()、poll()、epoll() などの非同期多重 IO を使用してミリ秒単位のタイムアウトを達成したりできます。レベルタイムアウト。これを実現するために、セカンダリのカプセル化された非同期 IO ライブラリ (libevent、libev) を使用することもできます。
1. アラーム内のシグナルを使用してタイムアウト (第 2 レベルのタイムアウト) を実装します
注: Linux カーネルの接続タイムアウトは通常 75 秒です。接続から早く戻るために 10 秒などの短い時間を設定できます。ここでは信号処理メカニズムを使用し、アラームを呼び出し、タイムアウト後に SIGALRM 信号を生成します (select を使用して実装することもできます)
alarim を使用して接続設定タイムアウトのコード例を数秒で実装します:
//信号処理関数
静的 void connect_alarm(intsigno)
{
debug_printf("シグナルハンドラー");
戻る;
}
//アラームタイムアウト接続の実装
静的 void conn_alarm()
{
Sigfunc * sigfunc; //既存の信号処理関数
sigfunc=signal(SIGALRM, connect_alarm); // 信号処理関数 connect_alarm を作成し、(存在する場合) 既存の信号処理関数を保存します
int タイムアウト = 5;
//目覚まし時計をセットします
if( アラーム(タイムアウト)!=0 ){
//...アラームが設定され、処理されました
}
//接続操作を実行します
If (connect(m_Socket, (struct sockaddr *)&addr, sizeof(addr)) If (errno == EINTR) { //エラー番号が EINTR に設定されている場合、タイムアウト割り込みを意味します
debug_printf("タイムアウト");
m_connectionStatus = STATUS_CLOSED;
errno = ETIMEDOUT // スリーウェイ ハンドシェイクの続行を禁止します
;
return ERR_TIMEOUT;
}
else {
debug_printf("その他のエラー");
m_connectionStatus = STATUS_CLOSED;
return ERR_NET_SOCKET;
}
}
alarm(0);//時計を止める
signal(SIGALRM, sigfunc); // (存在する場合) 元の信号処理関数を復元します
戻ります;
}//データ読み取りのタイムアウト設定
また、recv のタイムアウトを設定することもでき、5 秒以内に応答が受信されない場合は中断されます
シグナル(…);
アラーム(5);
受信(…);
アラーム(0);
static void sig_alarm(int Signo){return;}
クライアントが読み取り (readline など) でブロックされている場合、この時点でサーバーがクラッシュすると、クライアントの TCP はサーバーから ACK の受信を試み、データ セグメントの再送信を続行します。これには約 9 分かかります。再送信を開始し、エラーを返します。したがって、クライアントが読み取り時にブロックされると、呼び出しはタイムアウトになります。
2. 非同期多重 IO 使用量を使用する (ミリ秒タイムアウト)
非同期IO実行プロセス:
1. まずフラグをノンブロッキングモードに設定し、ノンブロッキングモードでconnect関数を呼び出す準備をします
2. connect を呼び出します。通常の状況では、TCP スリーウェイ ハンドシェイクには時間がかかるため、すぐに完了できない限り、ここで EINPROGRESS が返されます。確立されていますが、完了していません。
3. 現在のソケットを読み取りソケット記述子セット (fd_set rset) と書き込みソケット記述子セット (fd_set wset) に設定し (FD_ZERO()、FD_SET() マクロを使用)、タイムアウトを設定します (struct timeval *timeout)
4. select(ソケット、&rset、&wset、NULL、タイムアウト)を呼び出します
0 が返される場合は、接続がタイムアウトしたことを示します。設定したタイムアウトが 75 秒を超えている場合、カーネルの接続のタイムアウト制限は 75 秒であるため、これを行う必要はありません。
//ミリ秒タイムアウトの実装を選択する例:
静的 void conn_select() {
// TCP ソケットをオープンします
m_Socket = ソケット(PF_INET,SOCK_STREAM,0);
If( m_Socket
{
m_connectionStatus = STATUS_CLOSED;
return ERR_NET_SOCKET;
}
struct sockaddr_in addr;
inet_aton(m_Host.c_str(), &addr.sin_addr);
addr.sin_port = htons(m_Port);
addr.sin_family = PF_INET;
// ソケットのタイムアウト値を設定します
struct timeval タイムアウト;
Timeouts.tv_sec = SOCKET_TIMEOUT_SEC // const -> 5
;
Timeouts.tv_usec = SOCKET_TIMEOUT_USEC // const -> 0
;
uint8_t optlen = sizeof(タイムアウト);
If( setsockopt( m_Socket, SOL_SOCKET, SO_RCVTIMEO,&timeouts,(socklen_t)optlen)
{
m_connectionStatus = STATUS_CLOSED;
return ERR_NET_SOCKET;
}
// ソケットを TCP Nolay に設定します (送信/書き込みコマンドの直後に送信します)
int flag_TCP_nolay = 1;
If ( (setsockopt( m_Socket, IPPROTO_TCP, TCP_NODELAY,
)
(char *)&flag_TCP_nolay、sizeof(flag_TCP_nolay)))
{
m_connectionStatus = STATUS_CLOSED;
return ERR_NET_SOCKET;
}
// ソケットフラグを保存します
int opts_blocking = fcntl(m_Socket, F_GETFL);
If ( opts_blocking
{
return ERR_NET_SOCKET;
}
//ノンブロッキングモードに設定します
int opts_noblocking = (opts_blocking | O_NONBLOCK);
// ソケットをノンブロッキングに設定します
if (fcntl(m_Socket, F_SETFL, opts_noblocking)
{
ERR_NET_SOCKET を返します;
}
// 接続します
if ( connect(m_Socket, (struct sockaddr *)&addr, sizeof(addr))
{
// ノンブロッキング接続では EINPROGRESS が常に表示されます
if ( errno != EINPROGRESS )
{
m_connectionStatus = STATUS_CLOSED;
ERR_NET_SOCKET を返します;
}
// select 用のソケットのセットを作成します
fd_set ソックス;
FD_ZERO(&ソックス);
FD_SET(m_Socket,&socks);
// 接続を待つかタイムアウトします
int fdcnt = select(m_Socket+1,NULL,&socks,NULL,&timeouts);
if ( fdcnt
{
ERR_NET_SOCKET を返します;
}
else if ( fdcnt == 0 )
{
ERR_TIMEOUT を返します;
}
}
//ソケットを再度ブロッキングに設定します
if(fcntl(m_Socket,F_SETFL,opts_blocking)
{
ERR_NET_SOCKET を返します;
}
m_connectionStatus = STATUS_OPEN;
0 を返す;
}
説明: 超高速処理に関しては、特にスクリプトの説明ではありません: 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 の超過時間は、PHP 自体が接続する後端 (MySQL、HTTP、Memcached) の超過時間よりも大きくなりますが、前と同じです。
2.超時間設置原は何ですか?
永久に超過しないコード (上記のプログラムや定期的なプログラムなど) を希望する場合は、主に 1 つの PHP プログラムまたは後端を永久に保護するために、12 時間などの超過時間を設定することを計画しています。他の部分からのサービスの提供ができなくなり、最終的にはすべてのマシンの雪崩が発生します。
高速な応答が必要なプログラムの場合は、接続 500 ミリ秒、書き込み 1 秒、書き込み 1 秒などの速度で後端を設定することにより、アプリケーション雪崩の問題を大幅に軽減し、サーバーの負荷が高くなりすぎないようにすることができます。
3. 自己开公開超時間访问合适吗?
一般的に、万が得られない限り、構築用に非常に多くのネットワーク プログラム フレームがあり、ベースのパッケージも優れており、田舎の一般的なアプリケーションには、いくつかのネットワーク IO のライブラリと比較して、大量の内部配置があり、自己再構築が容易です。バグがあるため、これも不便です (学術的な目的に基づくものであるとは限りません)。
4. その他の建议
タイムアウトはすべてのアプリケーションにおいて大きな問題となるため、アプリケーションを開発する際には考慮する必要があります。いくつかのアプリケーションでタイムアウトが数百秒に設定されているのを見たことがありますが、このパフォーマンスは非常に悪いです:
たとえば、php-fpm が 128 個の php-cgi プロセスを開いていて、タイムアウトが 32 秒に設定されている場合、バックエンド サービスが貧弱な場合、極端な場合には、1 秒あたりに応答できるリクエストの最大数が制限されます。は:
128 / 32 = 4
文字通り、1 秒間に 4 つのリクエストしか処理できないため、サービスが悪すぎます。 php-cgi プロセスのサイズを増やすことはできますが、メモリ使用量やプロセス間のスイッチングコストも増加し、サービスが不安定になります。したがって、適切なタイムアウト値を設定するか、バックエンドのパフォーマンスを向上させるようにしてください。