最近、PHP マシンが頻繁に過負荷になり、リクエストが送信される限り、リクエストの処理を担当する PHP プロセスが CPU を 100% 占有します。本来の負荷分散戦略は、特定のマシンの PHP リクエストで接続タイムアウトが発生すると、そのマシンの重みを軽減することであり、ある程度の遅延効果はありますが、そのマシンにリクエストが送信される確率は減少するはずです。最終的には圧力を軽減し、最終的にはサービスを回復することができましたが、この戦略は最近突然失敗しました。この状況が発生すると、php-fpm に送信できないリクエストは、リクエストが空の php ファイルであっても cpu100% になります。したがって、アクセルが原因である可能性があると推測しました。
Php-fpm の request_terminate_timeout は 5 秒に設定されているため、リクエストが 5 秒を超えて実行される限り、php-fpm は問題が発生する前後で多数の 5 秒のタイムアウトが発生しました。最初の推測では、これは eaccelerator が原因である可能性があります。これは、子プロセスが強制終了されたときに共有メモリが正しく書き込まれず、すべてのリクエストでエラーが発生したことが原因であると考えられていました。ただし、これでは新しいファイルが発生する問題は説明できませんでした。それで私はeaccelaotrのコードを見に行き、次のコードを見つけました
[CP]
#define pinlock_try_lock(rw) asm volatile("lock ; decl %0" :"=m" ((rw)->lock) : : "memory")
#define _spinlock_unlock(rw) asm volatile("ロック ; %0 を含む" :"=m" ((rw)->lock) : : "メモリ")
static int mm_do_lock(mm_mutex* ロック、int 種類)
{
しながら (1) {
スピンロック_トライ_ロック(ロック);
if (lock->lock == 0) {
ロック->pid = getpid();
ロック->ロック = 1;
1 を返します。
_spinlock_unlock(ロック);
sched_yield();
}
1 を返す
}
static int mm_do_unlock(mm_mutex* lock) {
if (lock->locked && (lock->pid == getpid())) {
ロック->pid = 0;
ロック->ロック = 0;
_spinlock_unlock(ロック);
}
1 を返す
}
[CP]
mm_mutex は共有メモリを指しています。つまり、eac は共有メモリをプロセス間ロックとして使用し、スピンロック方式を使用しているため、すべてが説明できます。次の状況を想像してください。プロセスがロックを取得した後、php-fpm によって強制終了され、ロックが解除されないため、全員がこの while(1) 状態になります。ループの中。推測ができたところで、どうやってそれを確認できるでしょうか?当初のアイデアは共有メモリを直接読み取ることでしたが、php が IPC_PRIVATE であることが判明したため、それを読み取る方法がありませんでした。そのため、オンラインで問題が発生するまで待って、GDB にアクセスしてメモリを確認する必要がありました。今日、ついに決定的な証拠を入手しました
。
[html]
(gdb) p *mm->ロック
$8 = {ロック = 4294966693、pid = 21775、ロック = 1}
ここでは、プロセス番号 21775 のプロセスによってメモリが取得されていることがわかりますが、実際には、このプロセスはかなり前に強制終了されています。
問題が確認されたので、この問題が発生する条件を振り返ってみましょう
1. リクエストの実行時間が非常に長く、php-fpm によって強制終了されてしまいます
2. プロセスが強制終了されると、php はファイルを要求し、eac がロックを取得します
ここから、この確率を増幅させる特定の状況がいくつかあることがわかります
1. request_terminate_timeout が非常に短いです
2. auoload メソッドを使用するか、実行ロジックでファイルを require します。リクエストが開始される前にすべてのファイルがロードされていれば、require ファイルだけがタイムアウトしない限り、ファイルが要求されたときにそのファイルが強制終了されるべきではないからです。しかし、同じ autoload メソッドを使用してこの問題を回避する醜い方法もあります。それは、実行時間が長すぎる場合に、autoload 関数で判断することです。require
の代わりに終了します。
個人的には、この問題を解決する最善の方法は、request_terminate_timeout 時間を 30 秒、300 秒など十分に長く設定し、すべてのタイムアウト判定をアプリケーション層に置くことだと思います。この種の問題は php-fpm では解決できません。実際、php-fpm のみ。これは最後の手段の保険として使用できるので、使用する必要があります。また、phpにはmax_execution_timeというタイムアウト設定がありますが、このタイムアウトはCGIモードのCPU時間なのであまり効果はありません
http://www.bkjia.com/PHPjc/477815.htmlwww.bkjia.com本当http://www.bkjia.com/PHPjc/477815.html技術記事最近、PHP マシンが頻繁に過負荷になり、リクエストが送信される限り、リクエストの処理を担当する PHP プロセスが CPU を 100% 占有します。元の負荷分散戦略...