DISABLE_WP_CRONをしてもNginx Cache Controllerプラグインがwp-cron.phpにリクエストし続ける件
2014/09/25 追記
この記事がプラグイン作者に補足された結果、現バージョン(2.8.0)では修正されています。やったね!
沼にハマってほぼ徹夜でございます。とほほ。
コトの発端
エロいサイトというのは夜が中心なのですが、その中でも週末は特に盛り上がるわけですよ。
で、週末になるとサイトが重たくなるので、金曜までに何とかしないとなと思ってやったのがここ最近の記事。
- WordPressで予約投稿が失敗する - エロサイトの作り方
- WordPressのwp-cron処理をLinuxのcronで行うようにする - エロサイトの作り方
- サイトの負荷状況 - エロサイトの作り方
- Nginxのリバースプロキシのキャッシュ置き場をRAMディスクにする - エロサイトの作り方
この時はピーク時のLoad Averageが2.77→1.52まで下がったので、 期待したほどの効果じゃないなーと思いながらも、これでよしとしたわけです。
そして金曜日になり、モニタリングしてたら、
金曜なのでDisk I/OとNetwork I/Oが高いのはまあわかるとして、CPU負荷がピークで79.2%とかなり高い。
しかもプロセス別で見るとPHPがピークで53.5%を占めている。
リバースプロキシーを使っているので、PHPへのアクセスは、
- キャッシュの有効期限が切れた
- 予約投稿時間になりキャッシュがクリアされた(直近の投稿は21:40)
- RAMディスクがいっぱいになってキャッシュが消された
- cronからのcurlを使ったwp-cron.phpへのアクセス(2分おき)
くらいしか発生しないはずのため、この数字はおかしいと思った。
発生箇所探し
ここはさっくりとNginxのログを見たらすぐわかったのですが、wp-cron.php
へのアクセスが一般ユーザー経由っぽいのが混じってるんですね。
xxx.xxx.xxx.xxx - - [30/Aug/2014:00:08:01 +0900] "GET /wp-cron.php HTTP/1.0" 200 0 "http://hentai-kun.example.jp/archives/6281" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)"
xxx.xxx.xxx.xxx - - [30/Aug/2014:00:08:01 +0900] "GET /wp-cron.php HTTP/1.0" 200 0 "-" "curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3"
xxx.xxx.xxx.xxx - - [30/Aug/2014:00:08:14 +0900] "GET /wp-cron.php HTTP/1.0" 200 0 "http://hentai-kun.example.jp/archives/1890" "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) GSA/4.1.0.31802 Mobile/11D167 Safari/9537.53"
xxx.xxx.xxx.xxx - - [30/Aug/2014:00:08:40 +0900] "GET /wp-cron.php HTTP/1.0" 200 0 "http://hentai-kun.example.jp/archives/6666" "Opera/9.80 (Windows NT 6.1; WOW64) Presto/2.12.388 Version/12.17"
xxx.xxx.xxx.xxx - - [30/Aug/2014:00:08:49 +0900] "GET /wp-cron.php HTTP/1.0" 200 0 "http://hentai-kun.example.jp/" "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko"
2行目のリファラーがcurl/7.22.0
で始まっているものがcronからのアクセスで、残りはどう見ても一般ユーザーから。
Ajaxでwp-cron.php
へのアクセスを投げているっぽいので、レンダリングされたソースを見たところ、
<script type="text/javascript">
(function($){
$.get("http://hentai-kun.example.com/wp-cron.php");
})(jQuery);
</script>
という記述があった。
どうやら、wp_footer()
で書かれたものらしい。
wp_footer()の謎
ここまでは良かったのですが、ここから詰まってしまった。
そもそも、wp_footer()
って何を書き出しているの?、と。
そしてここからWordPressのソース追っかけ。
wp_footer()
do_action( 'wp_footer' );
add_action('wp_footer')
add_action( 'wp_footer', 'wp_print_footer_scripts', 20 );
wp_print_footer_scripts()
do_action( 'wp_print_footer_scripts' );
add_action('wp_print_footer_scripts')
add_action( 'wp_print_footer_scripts', '_wp_footer_scripts' );
_wp_footer_scripts()
print_late_styles();
print_footer_scripts();
wp_footer
とwp_print_footer_scripts
のアクションを呼び出しているようだ。
しかし、うーん、わからん。
wp-cron.phpで検索
wp_footer()
はひとまず置いておいて、どうせどこかに文字列コーディングされているだろうから、
wp-corn.php
で検索したらわかるのではないかと思って調べてみる。
そうしたら、wp-includes/cron.phpの2つの関数が引っかかった。
wp_cron()
// Prevent infinite loops caused by lack of wp-cron.php
if ( strpos($_SERVER['REQUEST_URI'], '/wp-cron.php') !== false || ( defined('DISABLE_WP_CRON') && DISABLE_WP_CRON ) )
return;
1つ目のwp_cron()
は、開始早々defined('DISABLE_WP_CRON')
が定義されてたら終了する処理だったので関係ない。
spawn_cron()
もう1つのspawn_cron()
は、
$cron_request = apply_filters( 'cron_request', array(
'url' => add_query_arg( 'doing_wp_cron', $doing_wp_cron, site_url( 'wp-cron.php' ) ),
'key' => $doing_wp_cron,
'args' => array(
'timeout' => 0.01,
'blocking' => false,
/** This filter is documented in wp-includes/class-http.php */
'sslverify' => apply_filters( 'https_local_ssl_verify', true )
)
) );
wp_remote_post( $cron_request['url'], $cron_request['args'] );
ここら辺の処理があやしいものの、wp_cron()
からしか呼ばれないのでここも実行されなそう。
もしかして、DISABLE_WP_CRONが効いてないのでは?
と思って、wp_cron()
を
function wp_cron() {
return;
...
}
のようにいきなりreturnにしてみたところ、変わらず。
うーん、どうやらWordPress本体で処理しているものではなさそう。
ということは、プラグインが怪しい
と思って、プラグインをwp-cron.php
で検索したところ、
Nginx Cache Controllerプラグインで、
public function wp_print_footer_scripts_wp_cron(){
$js = '
<script type="text/javascript">
(function($){
$.get("%s");
})(jQuery);
</script>
';
$js = sprintf($js, site_url('wp-cron.php'));
echo apply_filters('wp_print_footer_scripts_wp_cron', $js);
}
という記述を見つける。間違いなくこれだ!
Nginx Cache Controller
というわけで、ソースの追っかけ。
wp_print_footer_scripts_wp_cron()
if ($this->is_future_post()) {
wp_enqueue_script('jquery');
add_action(
"wp_print_footer_scripts",
array(&$this, "wp_print_footer_scripts_wp_cron")
);
}
add_action('wp_print_footer_scripts')
public function wp_enqueue_scripts() {
...
if ($this->is_future_post()) {
wp_enqueue_script('jquery');
add_action(
"wp_print_footer_scripts",
array(&$this, "wp_print_footer_scripts_wp_cron")
);
}
}
wp_enqueue_scripts()
add_action(
'wp_enqueue_scripts',
array($this, 'wp_enqueue_scripts')
);
wp_enqueue_script
アクションで、wp_print_footer_scripts
アクションを定義して、
その中でwp-cron.php
のリクエストのコードを出力しているようだ。
remove_action('wp_print_footer_scripts')で消す(失敗)
対応としては、add_action()
しているものにremove_action()
をかければいいわけだけど、
remove_action()
には引数として実行する関数名を指定しないといけない。
しかし、
add_action(
"wp_print_footer_scripts",
array(&$this, "wp_print_footer_scripts_wp_cron")
);
なんか配列だし、thisの参照を渡しているし、これをどうやって指定すればいいのか?
add_actionの第2引数が配列な理由
add_action フックをクラスを使って構築されたプラグインやテーマ内で使うには、add_action コール内にそのクラスの関数名とともに $this を追加してください。
どうやら、クラスのインスタンスに含まれる関数を呼ぶ場合はarrayを使うということらしい。
以下はその例です。
class MyPluginClass {
public function __construct() {
//add your actions to the constructor!
add_action( 'save_post', array( $this, 'myplugin_save_posts' ) );
}
public function myplugin_save_posts() {
//do stuff here...
}
}
なるほど。
functions.phpに追記
というわけで、必要な処理はこうなりました。
add_action('wp_enqueue_scripts', 'remove_nginxchampuru_cron', 100);
function remove_nginxchampuru_cron() {
global $nginxchampuru_cache;
remove_action('wp_print_footer_scripts', array(&$nginxchampuru_cache, 'wp_print_footer_scripts_wp_cron'));
}
wp_enqueue_script
でNginx Cache Controllerが処理した後で、wp_print_footer_scripts_wp_cron
を除去しています。
これで、wp-cron.php
が過剰に呼ばれなくなった。
しかし……
本来は、
if ($this->is_future_post()) {
wp_enqueue_script('jquery');
add_action(
"wp_print_footer_scripts",
array(&$this, "wp_print_footer_scripts_wp_cron")
);
}
が、
if ($this->is_future_post() && (!defined('DISABLE_WP_CRON') || !DISABLE_WP_CRON)) {
wp_enqueue_script('jquery');
add_action(
"wp_print_footer_scripts",
array(&$this, "wp_print_footer_scripts_wp_cron")
);
}
こうなっているべきだよね。(たぶん)