アルファサード株式会社 代表取締役 野田 純生のブログ


静的生成と動的生成, Webページをビルドするコストは最初の訪問者に支払ってもらいましょう。


公開日 : 2007-06-25 20:05:59


追記(2007年6月29日):
一連のエントリーで作成したものを取りまとめて公開しました。


もうちょっとだけ掘り下げてみたいと思います。というかあれこれ考えているよりTryしてみる方が早いし建設的かな。WordPressは触ったことがないのですがMTならこんな解決法もあるよ、ということで。

関連エントリー

このエントリーを書くきっかけとなった他の記事

Movable Type10分間Cooking!

再構築はインデックスやRSS, カテゴリー・アーカイブ等に限定し、エントリー・アーカイブはダイナミック生成とする。但し最初のリクエストがあった時にスタティックなファイルを生成してしまう(DBにキャッシュを保存するのではなく、そのhtmlを生成してしまう)。

※まだクライアントキャッシュのところ出来ていないのでGoogleさんに来られると嫌なので rel="nofollow" 付きリンクですが, テスト用サイトを用意してみた。そのうち削除するかも。

  • テスト用ブログ : Junnama Online (Dynamic)

まず現状のサイト(エントリー数250程度)をMTからエクスポート、新規ブログを作成してインポート。テンプレートは適当。

  1. 「設定」→「公開」→「再構築オプション」で「テンプレート別に、スタティックHTMLもしくはダイナミック・パブリッシングを選択します」にチェック。
    設定→公開→再構築オプション
  2. エントリー・アーカイブの編集画面で「このテンプレートをダイナミック・ページにする」にチェック。
    エントリー・アーカイブの編集画面
  3. サイト全体を再構築(インデックスやカテゴリー関係のみの再構築なので軽い。7秒くらいで完了)
  4. mtview.cgiを用意。実行権限を与える。
    
    #!/usr/bin/perl -w
    use strict;
    
    use lib qw (/path/to/mt/lib/);
    use lib qw (/path/to/mt/extlib/);
    
    use MT;
    use MT::Blog;
    use MT::Entry;
    use MT::Builder;
    use MT::Template;
    use MT::TemplateMap;
    use MT::Template::Context;
    
    my $mt = MT->new(Config => '/path/to/mt/mt-config.cgi');
    
    my $base_url = 'http://junnama.alfasado.net';
    my $base_pth = '/path/to/htdocs';
    my $blog_id = 3;
    
    my $request = $ENV{"REDIRECT_URL"};
    
    # /online/2007/06/mt4Movable Typebookmarklet.html
    
    my $permalink = $base_url.$request;
    my @pathes = split(/¥//,$permalink);
    my $counter = @pathes;
    my $lastpth = $pathes[$counter-1];
    my @basenames = split(/¥./,$lastpth);
    my $base_len = @basenames;
    my $basename = '';
    my $lastnum = $base_len-1;
    for (my $i = 0; $i < $lastnum; $i++) {
    	$basename .= $basenames[$i];
    	if ($lastnum-1 != $i) {
    		$basename .= '.';
    	}
    }
    
    # basename  => mt4Movable Typebookmarklet
    
    my $blog = MT::Blog->load({ id => $blog_id });
    my $iter = MT::Entry->load_iter({
    	blog_id => $blog_id,
    	basename => $basename},{
    	sort => 'modified_on',
    	direction => 'descend',
    });
    if (! defined $iter) {
    	$iter = MT::Entry->load_iter({
    		blog_id => $blog_id},{
    		sort => 'modified_on',
    		direction => 'descend',
    	});	
    }
    my $build_entry;
    while (my $entry = $iter->()) {
    	if ($entry->permalink eq $permalink){
    		$build_entry = $entry;
    		last;
    	}
    }
    
    # read template
    if (defined $build_entry) {
    	my $tmap = MT::TemplateMap->load(
    							{	blog_id => $blog_id,
    								archive_type => 'Individual',
    								is_preferred => 1
    							},);
    	my $template = MT::Template->load({id => $tmap->template_id});
    	my $preview_tmpl = $template->text;
    	my $ctx = MT::Template::Context->new;
    	$ctx->stash('entry', $build_entry);
    	$ctx->stash('blog', $blog);
    	$ctx->stash('blog_id', $blog_id);
    
    # build entry
    	my $build = MT::Builder->new;
    	my $tokens = $build->compile($ctx, $preview_tmpl);
    	my $html = $build->build($ctx, $tokens);
    	
    	print "content-type: text/html¥n¥n";
    	
    	if ($html) {
    # replay and write static file
    		print $html;
    		my $buildfile = $base_pth.$request;
    		open (OUT,">$buildfile");
    		print OUT $html;
    		close(OUT);
    	}
    } else {
    	print "content-type: text/html¥n¥n";
    	print "404 Not Found.";
    }
  5. MTが生成した.htaccessファイルがサイトのルートディレクトリにあるので削除。 新たに以下の1行のみ記述した.htaccessを生成。
    ErrorDocument 404 /dynamic/mtview.cgi

10分じゃ...終わらなかったけど、まぁ30分コース。

再構築時にはインデックス、カテゴリーアーカイブ等は再構築され、エントリー・アーカイブの再構築は行われない。

最初にリクエストがあったときに「ファイルが存在しなければ」NotFound、つまりmtview.cgiにリクエストが渡されるので、ページを生成してクライアントに返す(同時にファイルを生成する)。

アクセスがあるかどうかわからない古い奥の階層のページまで再構築する必要がないし、データベースへのアクセスは最初の1リクエストのみ、以後は生成されたスタティックなページが使われるのでCGIの起動もデータベースへのアクセスもない。

残りタスク

もちろんこれで完成ではない。「再構築」の代わりに「全消去」のプログラムが必要になる。ただMTのプラグインとか書いたことのある方ならこの処理は簡単なことがわかるだろう。

全消去が出来たらとりあえず運用は可能だけど、「ページをビルドするコストは最初に見た人に支払ってもらう」どころじゃなくてこのままじゃGoogleが全再構築してしまうので、あといくつかの工夫とチューニングが必要。

絶対にやっておきたいのはクライアント・キャッシュを有効に使う方法。
更新されていないページへGoogleが2回目以降のアクセスにやってきたらステータス304を適切に返すこと。

あとは、Permalinkからエントリーを特定するのが非効率的なので別途Permalinkとentry_idを紐付けるようなデータベーステーブルを作っておけば処理も速くなるでしょう。

その他にも再構築の範囲や全消去する時にどこまでを対象とするかを検討したり(このあたりはまさにサイトの内容とポリシーによって最適解を検討していけば良いと思う)。

WordPressだMTだってのは「ダイナミックだスタティックだ」ってな議論とは別の部分で行うべき。逆にWordPressでもこのような考え方をすればスタティックなファイル生成も割と簡単に実現できるんじゃないだろうか。

以上、Movable Type30分Cookingのコーナーでした(エントリー書くのに15分かかった...)

続きあります。



このブログを書いている人
野田純生の写真
野田 純生 (のだ すみお)

大阪府出身。ウェブアクセシビリティエバンジェリスト。 アルファサード株式会社の創業者であり、現役のプログラマ。経営理念は「テクノロジーによって顧客とパートナーに寄り添い、ウェブを良くする」。 プロフィール詳細へ