ホームページ  >  記事  >  バックエンド開発  >  PHP タイムアウト処理の包括的な概要

PHP タイムアウト処理の包括的な概要

WBOY
WBOYオリジナル
2016-06-13 13:19:031041ブラウズ

【独自】PHPタイムアウト処理総合まとめ

【独自】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 接続構成を変更します。

テーブル> デフォルトの設定は 30 秒です。独自の設定をカスタマイズする必要がある場合は、設定を変更する必要があります。たとえば、100 秒に変更します: (変更後に Apache を再起動します):

FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock

    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

ScriptAlias /fcgi-bin/ "/home/forum/apache/apache_php/cgi-bin/"

AddHandler php-fastcgi .php


アクション php-fastcgi /fcgi-bin/php-cgi

    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

 

AddType application/x-httpd-php .php
テーブル>

タイムアウトになると、500 エラーが返され、バックエンド PHP サービスを切断し、Apache エラー ログを記録します。

[Thu Jan 27 18:30:15 2011] [エラー] [クライアント 10.81.41.110] FastCGI: サーバー "/home/forum/apache/apache_php/cgi-bin/php-cgi" との通信が中止されました: アイドル タイムアウト(30秒)

[Thu Jan 27 18:30:15 2011] [エラー] [クライアント 10.81.41.110] FastCGI: サーバー「/home/forum/apache/apache_php/cgi-bin/php-」から不完全なヘッダー (0 バイト) を受信しましたcgi"

その他の fastcgi 設定パラメータの説明:

FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock -idle-タイムアウト 100 ScriptAlias /fcgi-bin/ "/home/forum/apache/apache_php/cgi-bin/" AddHandler php-fastcgi .php アクション php-fastcgi /fcgi-bin/php-cgi AddType application/x-httpd-php .php
テーブル>

[ Lighttpd ]

設定: lighttpd.conf

Lighttpd 設定では、タイムアウトに関連するパラメータは次のとおりです (スペースを考慮して、書き込みタイムアウトと読み取りタイムアウトのみが使用されます。書き込みタイムアウト パラメータについても同様です)。

IdleTimeout アイドル時間制限
IdleTimeout 发呆时限

ProcessLifeTime 一个进程的最长生命周期,过期之后无条件kill

MaxProcessCount 最大进程个数

DefaultMinClassProcessCount 每个程序启动的最小进程个数

DefaultMaxClassProcessCount 每个程序启动的最大进程个数

IPCConnectTimeout 程序响应超时时间

IPCCommTimeout 与程序通讯的最长时间,上面的错误有可能就是这个值设置过小造成的

MaxRequestsPerProcess 每个进程最多完成处理个数,达成后自杀

ProcessLifeTime はプロセスの最長のライフサイクルであり、期限切れ後に無条件に強制終了されます

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

--------------------------------------------------

主に関連するオプション: server.max-keep-alive-idle = 5 server.max-read-idle = 60 server.read-timeout = 0 server.max-connection-idle = 360 テーブル>

手順:

キープアライブ接続での連続したリクエストの場合、最初のリクエスト内容を送信する最大間隔はパラメータ max-read-idle によって決まります。2 番目以降のリクエスト内容を送信する最大間隔は次のように決まります。パラメータ max-keep -alive-idle の決定。リクエスト間のタイムアウトも max-keep-alive-idle によって決まります。リクエストコンテンツの送信の合計タイムアウトは、パラメータ read-timeout によって決まります。 Lighttpd がバックエンドと対話するためのタイムアウトは、max-connection-idle によって決まります。

詳細な読書:

http://www.snooda.com/read/244

[ Nginx ]

設定: nginx.conf

----------------------------------------------- --- --- # 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 ----------------------------------------------- --- ---
テーブル>

手順:

Nginx のタイムアウト設定は非常に明確で理解しやすいです。上記のタイムアウトはさまざまな動作モードに対応していますが、タイムアウトによって引き起こされる問題は数多くあります。

詳細な読書:

http://hi.baidu.com/pibuchou/blog/item/a1e330dd71fb8a5995ee3753.html

http://hi.baidu.com/pibuchou/blog/item/7cbccff0a3b77dc60b46e024.html

http { 

    #Fastcgi: (针对后端的fastcgi 生效, fastcgi 不属于proxy模式)

    fastcgi_connect_timeout 5;    #连接超时

    fastcgi_send_timeout 10;       #写超时

    fastcgi_read_timeout 10;        #读取超时

 

    #Proxy: (针对proxy/upstreams的生效)

    proxy_connect_timeout 15s;    #连接超时

    proxy_read_timeout 24s;          #读超时

    proxy_send_timeout 10s;         #写超时

}

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 秒; #書き込みタイムアウト

}

//...

  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的进程数量

  128

 

 

  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 为超时的秒数

  0s

 

  The timeout (in seconds) for serving of single request after which a php backtrace will be dumped to slow.log file

  '0s' means 'off'

  0s

 

 

http://hi.baidu.com/pibuchou/blog/item/10a549818f7e4c9df703a626.html http://www.apoyl.com/?p=466 [PHP自体のタイムアウト処理] [ PHP-fpm ] 設定: php-fpm.conf テーブル>

手順:

php.ini には、PHP スクリプトの最大実行時間を設定できるパラメータ max_execution_time がありますが、php-cgi (php-fpm) では、このパラメータは有効になりません。 PHP スクリプトの最大実行時間を実際に制御できます:

<値の名前="request_terminate_timeout">0s

つまり、max_execution_time は、mod_php5.so モードで実行すると有効になりますが、php-fpm モードで実行すると有効になりません。

詳細な読書:

http://blog.s135.com/file_get_contents/

[ PHP ]

設定: php.ini

オプション:

最大実行時間 = 30

またはコードに設定します:

ini_set("max_execution_time", 30);

set_time_limit(30);

手順:

現在のセッションに有効です。たとえば、0 を設定してもタイムアウトになりませんが、PHP のセーフモードがオンになっている場合、これらの設定は有効になりません。

効果は同じですが、php-fpm で request_terminate_timeout が設定されている場合、特定のコンテンツは php-fpm 部分を参照する必要があります。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 秒です。

カール通常の 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); ミリ秒レベルのタイムアウト設定をサポートできます

curl のミリ秒タイムアウトの例:

//... 処理される同時リクエストの数の制限を設定します。 Apache MaxClients ディレクティブと同等です。 元の php.fcgi の PHP_FCGI_CHILDREN 環境に相当します 任意の pm_style と一緒に使用されます。 #php-cgi プロセス数 <値の名前="max_children">128 ワーカー プロセスが終了するまでの単一リクエストの処理のタイムアウト (秒単位) 'max_execution_time' ini オプションが何らかの理由でスクリプトの実行を停止しない場合に使用する必要があります 「0」は「オフ」を意味します #php-fpm リクエスト実行タイムアウト、0s はタイムアウトしないことを意味します。それ以外の場合は、タイムアウトの秒数として Ns を設定します 0s 単一リクエストの処理のタイムアウト (秒単位)。その後、php バックトレースがslow.log ファイルにダンプされます 「0」は「オフ」を意味します 0s

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_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";

}

} else {

// サーバー

スリープ(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() を使用して次の処理を行います。

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

?>

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']) { 「接続がタイムアウトしました!」をエコー } else { $data をエコー } } テーブル> file_get_contents タイムアウト: テーブル>

fopen タイムアウト:

$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);

}

?>

$timeout = array(

'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";

?>

MYSQL_OPT_WRITE_TIMEOUT=12; これら 2 つを定義した後、オプションを使用して対応する値を設定できます。 ただし、mysql の内部実装には注意すべき点があります。 1. タイムアウト設定単位は秒、最小構成は 1 秒です 2. ただし、基礎となる mysql の読み取りは 2 回再試行されるため、実際の時間は 3 秒になります 2 回の再試行 + 1 回の再試行 = タイムアウトの 3 倍。つまり、最小タイムアウトは 3 秒であり、この値を下回ることはありません。ほとんどのアプリケーションでは許容されますが、少数のアプリケーションでは最適化が必要です。 mysql へのアクセスのタイムアウトを設定する PHP インスタンスを表示します:

詳細な読書:

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 のタイムアウト設定は、5 ミリ秒や 10 ミリ秒など、より小さく設定できますが、この時間を超える場合は、データベースからクエリを実行することをお勧めします。

次に、セット データの接続と読み取りのタイムアウトの C++ の例を示します。

//独自の読み取りおよび書き込みタイムアウト定数を定義します<🎜> <🎜>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 使用率が高くなります)

<本体>
$host = "127.0.0.1";

$port = "80";

$timeout = 15; //timeout in seconds

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)

or die("Unable to create socketn");

socket_set_nonblock($socket) //务必设置为阻塞模式

or die("Unable to set nonblock on socketn");

$time = time();

//循环的时候每次都减去相应值

while (!@socket_connect($socket, $host, $port)) //如果没有连接上就一直死循环

{

$err = socket_last_error($socket);

if ($err == 115 || $err == 114)

{

if ((time() - $time) >= $timeout)    //每次都需要去判断一下是否超时了

        {

          socket_close($socket);

          die("Connection timed out.n");

        }

        sleep(1);

        continue;

      }

      die(socket_strerror($err) . "n");

    }

    socket_set_block($this->socket)    //还原阻塞模式

      or die("Unable to set block on socketn");

?> 

$host = "127.0.0.1"; $port = "80"; $timeout = 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) //毎回タイムアウトしたかどうかを確認する必要があります { socket_close($socket); 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() を使用してタイムアウトを実装します

socket_select(..., Floor($timeout), ceil($timeout*1000000));


select の特徴: タイムアウトをマイクロ秒レベルで設定可能!

socket_select() のタイムアウト コードを使用します (理解するには非同期 IO プログラミングの知識が必要です)

テーブル>

[C&C++ でのタイムアウトの実装]

Linux C/C++ では通常、alarm() を使用してタイマーを設定して第 2 レベルのタイムアウトを達成するか、select()、poll()、epoll() およびその他の非同期多重化 IO を使用してミリ秒を達成できます。レベルタイムアウトタイムアウト。これを実現するために、セカンダリのカプセル化された非同期 IO ライブラリ (libevent、libev) を使用することもできます。

1. アラームでシグナルを使用してタイムアウト (第 2 レベルのタイムアウト)

を実装します。

注: Linux カーネルの接続タイムアウトは通常 75 秒ですが、接続から早く戻るまでの時間を 10 秒など短く設定することもできます。ここでは信号処理メカニズムを使用し、アラームを呼び出し、タイムアウト後に SIGALRM 信号を生成します (select を使用して実装することもできます)

alarym を使用して数秒で接続を実装します。タイムアウトを設定するコード例:

### 调用类 ####

$server = new Server;

$client = new Client;

for (;;) {

foreach ($select->can_read(0) as $socket) { 

 

    if ($socket == $client->socket) { 

      // New Client Socket 

      $select->add(socket_accept($client->socket)); 

    } 

    else { 

      //there's something to read on $socket 

    } 

  } 

?> 

 

### 异步多路复用IO & 超时连接处理类 ###

class select {

var $sockets;

function select($sockets) {

$this->sockets = array(); 

 

    foreach ($sockets as $socket) { 

      $this->add($socket); 

    } 

  } 

 

  function add($add_socket) { 

    array_push($this->sockets,$add_socket); 

  } 

 

  function remove($remove_socket) { 

    $sockets = array(); 

 

    foreach ($this->sockets as $socket) { 

      if($remove_socket != $socket) 

        $sockets[] = $socket; 

    } 

 

    $this->sockets = $sockets; 

  } 

 

  function can_read($timeout) { 

    $read = $this->sockets; 

    socket_select($read,$write = NULL,$except = NULL,$timeout); 

    return $read; 

  } 

 

  function can_write($timeout) { 

    $write = $this->sockets; 

    socket_select($read = NULL,$write,$except = NULL,$timeout); 

    return $write; 

  } 

?> 

### クラス #### の呼び出し can_read(0) as $socket) { if ($socket == $client->socket) { // 新しいクライアントソケット $select->add(socket_accept($client->socket)); } else { //$socket に読むべきものがあります } } } ?> ### 非同期多重 IO およびタイムアウト接続処理クラス ### sockets = array(); foreach ($sockets as $socket) { $this->add($socket); } } 関数 add($add_socket) { array_push($this->sockets,$add_socket); } 関数の削除($remove_socket) { $sockets = array(); foreach ($this->ソケットを $socket として) { if($remove_socket != $socket) $sockets[] = $ソケット } $this->sockets = $sockets; } 関数 can_read($timeout) { $read = $this->ソケット; ソケット選択($read,$write = NULL,$excel = NULL,$timeout); $read を返します } 関数 can_write($timeout) { $write = $this->ソケット; ソケット選択($read = NULL,$write,$excel = NULL,$timeout); $write を返します } } ?>
テーブル>

//データ読み取りのタイムアウト設定

recv のタイムアウトを設定することもでき、5 秒以内に応答が受信されない場合は中断されます

シグナル( ... );

アラーム(5)

recv( ... );

アラーム(0);

static void sig_alarm(int Signo){return;}

クライアントが読み取り (readline など) でブロックされている場合、この時点でサーバーがクラッシュすると、クライアント TCP はサーバーから ACK の受信を試み、データ セグメントの再送信を続行します。約 9 時間がかかります。分が経過すると再送信が中止され、エラーが返されます。したがって、クライアントの読み取りがブロックされると、呼び出しはタイムアウトになります。

?

2. 非同期多重 IO の使用 (ミリ秒のタイムアウト)

非同期 IO 実行プロセス:

1. まずフラグをノンブロッキング モードに設定し、ノンブロッキング モードで接続関数を呼び出す準備をします

2. 接続を呼び出します。通常の状況では、TCP スリーウェイ ハンドシェイクには時間がかかるため、すぐに完了できない限り、ここで EINPROGRESS が返されます。接続は確立中ですが、完了していません。

3. 読み取りソケット記述子セット (fd_set rset) と書き込みソケット記述子セット (fd_set wset) に現在のソケットを設定し (FD_ZERO()、FD_SET() マクロを使用)、タイムアウト時間を設定します (struct timeval *timeout) )

4. select(socket, &rset, &wset, NULL, timeout) を呼び出します

0 が返される場合は、接続がタイムアウトしたことを示します。設定したタイムアウトが 75 秒を超えている場合、カーネルの接続のタイムアウト制限は 75 秒であるため、これを行う必要はありません。

//select ミリ秒タイムアウトの実装例:


//信号処理関数
//信号处理函数

static void connect_alarm(int signo)

{

     debug_printf("SignalHandler");

     return;

}

 

//alarm超时连接实现

static void conn_alarm()

  Sigfunc * sigfunc ; //现有信号处理函数

  sigfunc=signal(SIGALRM, connect_alarm); //建立信号处理函数connect_alarm,(如果有)保存现有的信号处理函数 

    int timeout = 5;

 

    //设置闹钟

  if( alarm(timeout)!=0 ){ 

    //... 闹钟已经设置处理

  } 

 

    //进行连接操作

    if (connect(m_Socket, (struct sockaddr *)&addr, sizeof(addr)) < 0 ) {

        if ( errno == EINTR ) { //如果错误号设置为EINTR,说明超时中断了

            debug_printf("Timeout");

            m_connectionStatus = STATUS_CLOSED;

            errno = ETIMEDOUT; //防止三次握手继续进行 

            return ERR_TIMEOUT;

        }

        else {

            debug_printf("Other Err");

            m_connectionStatus = STATUS_CLOSED;

            return ERR_NET_SOCKET;

        }

    }

  alarm(0);//关闭时钟 

  signal(SIGALRM, sigfunc); //(如果有)恢复原来的信号处理函数 

  return; 

}

static void connect_alarm(intsigno) { debug_printf("SignalHandler"); 戻る; } //アラームタイムアウト接続の実装 static 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; // スリーウェイ ハンドシェイクを続行しないようにします ERR_TIMEOUT を返す; } else { debug_printf("その他のエラー"); m_connectionStatus = STATUS_CLOSED; ERR_NET_SOCKET を返します; } } アラーム(0);//時計をオフにします signal(SIGALRM, sigfunc); // (存在する場合) 元の信号処理関数を復元します 戻る }
static void conn_select() {
// TCP ソケットをオープンします

m_Socket = ソケット(PF_INET,SOCK_STREAM,0);

if( m_Socket {

m_connectionStatus = STATUS_CLOSED;

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

uint8_t optlen = sizeof(タイムアウト);

if( setsockopt( m_Socket, SOL_SOCKET, SO_RCVTIMEO,&timeouts,(socklen_t)optlen)

{

m_connectionStatus = STATUS_CLOSED;

ERR_NET_SOCKET を返します;

}

// ソケットを TCP 遅延なしに設定します (送信/書き込みコマンドの直後に送信します)

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;

ERR_NET_SOCKET を返します;

}

// ソケットフラグを保存

int opts_blocking = fcntl(m_Socket, F_GETFL);

if ( opts_blocking {

ERR_NET_SOCKET を返します;

}

//ノンブロッキングモードに設定します

int opts_noblocking = (opts_blocking | O_NONBLOCK);

// ソケットを非ブロッキングに設定します

if (fcntl(m_Socket, F_SETFL, opts_noblocking)

{

return ERR_NET_SOCKET;

}

// 接続

if ( connect(m_Socket, (struct sockaddr *)&addr, sizeof(addr)) < 0)

{

// EINPROGRESS は常にノンブロッキング接続に表示されます

if ( errno != EINPROGRESS )

{

m_connectionStatus = STATUS_CLOSED;

return ERR_NET_SOCKET;

}

// 選択用の ソケット セットを作成します

fd_set ソックス;

FD_ZERO(&socks);

FD_SET(m_Socket,&socks);

// 接続またはタイムアウトを待つ

int fdcnt = select(m_Socket+1,NULL,&socks,NULL,&timeouts);

if ( fdcnt < 0 )

{

return ERR_NET_SOCKET;

}

else if ( fdcnt == 0 )

{

return ERR_TIMEOUT;

}

}

//ソケットを再度ブロックに設定します

if(fcntl(m_Socket,F_SETFL,opts_blocking)

{

return ERR_NET_SOCKET;

}

m_connectionStatus = STATUS_OPEN;

return 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 のタイムアウトは、バックエンド (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 プロセスのサイズを増やすことはできますが、メモリ使用量やプロセス間のスイッチングコストも増加し、サービスが不安定になります。したがって、適切なタイムアウト値を設定するか、バックエンドのパフォーマンスを向上させるようにしてください。

この記事には経験値や参考となる内容が含まれておりますので、不備がございましたらご指摘ください。


2F
maque03125 時間前
Retweeted
1F
my_yang 昨日 23:47
案の定、非常に包括的なまとめで、とても勉強になりました
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。