2010年9 月1日  |  Written by matsumoto  |  under Perl Yahoo!ブックマークに登録    はてなブックマーク - [perl] SledgeでText::Xslateを使う

こんにちは、松本です。
3月の最終エントリ以後、暑すぎて ダラダラし過ぎました。Blogをちゃんと書くようにします。

今回はperlのフレームワーク "Sledge" でテンプレートエンジンの "Text::Xslate" を使います。

Template-Toolkitについて

Sledgeで利用するテンプレートエンジンは TT(Template-Toolkit) がデファクトスタンダードであります。
(HTML::Templateも使えますが・・・)

TTは、テンプレートエンジンとして非常に柔軟性が高く、大変に使いやすいのですが何せ、処理が重いです。

6A(シックス・アパート)のBlogサービスの、VoxもTemplate-Toolkitを利用しているらしいのですが

Template Toolkit: profile. Wow! Template Toolkit takes 60% of the request time.

との事で、リクエストの処理全体の6割をTTが占有してしまっているという驚愕の事実らしいです。

弊社でも ClearSilverText::MicroTemplateTenjiin などを Sledge に組み込めるか、試行錯誤してみたのですが
TTと全く違う文法で記述されたHTMLテンプレートが、プロジェクト毎に混在するとなると、運用面で人的負荷や稼動コストが増える可能性があったため、見送っていました。


Text::Xslate

Text::Xslate は、藤 吾郎さん が CPAN で公開されているモジュールで、精力的にUPDATEされているテンプレートエンジンです。

TTとほとんど同一の文法でHTMLテンプレートを記述することができ、更に高速動作するモジュールでして
先の 6割の処理の部分を抑える事ができるのでは?と期待大です。

TTの文法により近づけるために Text::Xslate::Bridge::TT2Like モジュールも併せて利用します。

インストール

cpanm で さっくりインストールです。

cpanm -S Text::Xslate Text::Xslate::Bridge::TT2Like

Sledgeのテンプレートクラス

ズバリ、以下のテンプレートクラスをPages.pmから呼び出しています。

Prog/Pages.pm (一部)

シンタックスハイライトを付けてある、全コードは こちら

sub create_template {
    my ($self, $file) = @_;
    my $tmpl = Proj::Template::Text::Xslate->new($file, $self);
    return $tmpl;
}

Proj/Template/Text/Xslate.pm

シンタックスハイライトを付けてある、全コードは こちら

package Proj::Template::Text::Xslate;

use utf8;
use strict;
use warnings;
#use base qw/Sledge::Template/;

use Text::Xslate;
use Scalar::Util;

use vars qw($Tx);
$Tx = '';

sub new {
    my($class, $file, $page) = @_;
    bless {
        _options => {
            filename => $file,
        },
        _params  => {
            config  => $page->create_config,
            r       => $page->r,
            session => $page->session,
        },
    }, $class;
}

sub add_associate       { Sledge::Exception::UnimplementedMethod->throw }
sub associate_namespace { Sledge::Exception::UnimplementedMethod->throw }

sub output {
    my $self = shift;
    #my %config = %{$self->{_options}};
    #my $input  = delete $config{filename};
    my $input  = delete $self->{'_options'}->{'filename'}
                           || Sledge::Exception::TemplateNotFound->throw(
                                   "No template file detected. Check your template path.",
                              );
    if(!$Tx) {
        my %config = %{$self->{_options}};

        $config{syntax}    = 'TTerse';
        $config{header}    = [ qw/commons.inc/ ];
        $config{module}    = [ qw/Text::Xslate::Bridge::TT2Like/ ],
        $config{path}      = [ $self->{'_params'}->{'config'}->tmpl_path, $self->{'_params'}->{'config'}->tmpl_path . '/includes' ];
        $config{cache}     = 1;
        $config{cache_dir} = $self->{'_params'}->{'config'}->cache_dir . '/text_xslate_cache';
        $config{type}      = 'text';
        $config{function}  = {
            nl2br      => \&Text::Xslate::Bridge::TT2Like::html_line_break,
            uri_escape => \&Text::Xslate::uri_escape,
        };
        $Tx = Text::Xslate->new(%config);
        #warn "[ CREATE TX ]";
    }
    unless (-e $input) {
        Sledge::Exception::TemplateNotFound->throw(
             "No template file detected. Check your template path.",
        );
    }
    $input =~ s@/path/to/proj/view/@@;
    my $output = $Tx->render($input, $self->{'_params'});
    $self->finalize;
    return $output;
}

sub param {
    my $self = shift;
    if (@_ == 0) {
        return keys %{$self->{'_params'}};
    }
    elsif (@_ == 1) {
        return $self->{'_params'}->{$_[0]};
    }
    else {
        while (my($key, $val) = splice @_, 0, 2) {
            $self->{'_params'}->{$key} = $val;
        }
    }
}

sub finalize {
    my $self = shift;
    for my $key ($self->param()) {
        if( ! ref( $self->{'_params'}->{$key} ) || $key eq 'session' || $key eq 'r' || $key eq 'config') { next; }
        if (!Scalar::Util::isweak($self->{'_params'}->{$key})) {
            #warn "[ WEAKEN ] ".$key;
            eval { Scalar::Util::weaken($self->{'_params'}->{$key}) };
        }
        delete $self->{'_params'}->{$key};
    }
}

1;

速度比較

Template-ToolkitからText::Xslateに変更することで、大よそ 150% ぐらい、リクエストの処理速度が向上しました。

今まで 100req/sec だったものが 150req/sec に高速化された形です。

気になった点、ハマッた点

  • mod_perlのプロセスサイズが 120%ぐらい増えました。 メモリが少ない環境では要注意かも。

ハマッた点

Docsにも書いてある点がほとんどなので、ちゃんと読め!という部分なのですが・・・

  • [% INCLUDE "header.inc" %] など、 "(ダブルクォーテーション)でファイル名を囲まないといけないです。
  • TTのオプションに存在する、"ABSOLUTE" が使えないので INCLUDE内のファイルに明示的に変数を渡さなければならないので

    [%- INCLUDE "header.inc" WITH
        FOO = 'ふー'
     -%]

    などとWITHで記述します。

  • TTでいう、PROCESS、INSERT は使えないです。
  • マクロを呼び出すときに、[% macro_func %] でなく、[% macro_func() %] と () を付ける必要があります。
  • render($file,$param) で指定する $fileは path で指定された以下のファイルパスを指定します。(絶対パスではないです)
  • TTの PRE_PROCESS は Text::Xslate の new時に渡す引数の header に相当します

まとめ

Template-Toolkitとほとんど同一の記法で、基本差し替えるだけで
Webアプリ全体の処理速度が向上しますので、是非ご利用されてみてはいかがでしょうか。

150%の処理速度向上が見込めましたので、次回プロジェクトでは試行してみたいと思います。

2010年3 月18日  |  Written by matsumoto  |  under PHP Yahoo!ブックマークに登録    はてなブックマーク - [PHP] peclのmemcachedを使ってみる

peclでは memcacheモジュール が有名どころでありますが、 memcachedモジュール ("d"が最後にくっついた)というものもあり、こちらは libmemcached をベースにしたモジュールとの事です。

memcachedモジュールは digg の中の人が作成されたモジュールとの事で、リリース後まだ1年ぐらいですが
get_multiをサポートなど今後の期待大のモジュールです。

ということで早速インストールしてみます。

libmemcached のインストール

http://tangent.org/552/libmemcached.html
libmemcached 本体をDLします。

現時点(2010.3.18)では0.38が最新版ですのでこれをDLします。

※CentOS4でのインストール時には、gccのバージョンが違うためでしょうか、make時にエラーになりました。この場合にはlibmemcachedのバージョンを落として利用しました。(今回は0.32に落としたら通りました)

wget http://download.tangent.org/libmemcached-0.38.tar.gz
tar xvzf libmemcached-0.38.tar.gz
cd libmemcached-0.38

32bit環境だとそのままのconfigureでエラーが発生するので、configure で若干の指定を追加します。
こちらのBlog を参考にしました。

64bitの場合 (そのままで指定なし)

./configure

32bitの場合

./configure -disable-64bit CFLAGS="-O3 -march=i686"

make -> インストールします。

sudo make install

pecl memcached をインストール

以下から最新版のをDLします

※github版はこれですかね・・?

※memcached モジュールはdiggの中の人が作ったらしいです

pecl コマンドでインストール

一発でインストールです。楽ですね

sudo pecl install libmemcached

ソースからインストール

wget http://pecl.php.net/get/memcached-1.0.1.tgz
tar xvzf http://pecl.php.net/get/memcached-1.0.1.tgz
cd memcached-1.0.1
phpize
./configure
make install

PHPに認識させる

/etc/php.ini に以下を追加します

[memcached]
extension=memcached.so

mod_php でも認識させるため Apache(httpd)を再起動させます

sudo service httpd restart

確認する

phpinfoでmemcachedモジュールとlibmemcached のライブラリが認識されているか確認します

$ php -R ' phpinfo(); exit(); ' | grep memcached

memcached
memcached support => enabled
libmemcached version => 0.38
Registered save handlers => files user sqlite memcached tokyo_tyrant

ブラウザ越しに mod_php での挙動も確認します

<?php
phpinfo();

サンプルプログラム

弊社ではCacheクラスを作り、それをインスタンス化して利用しています。

コマンドラインでのスクリプト

きちんと実行されれば、 "hoge" と "array" をキーにした値が表示されると思います。

#!/bin/env php
<?php

ini_set('memory_limit','-1');
set_include_path(".");

include_once('./Proj_Cache.class.php');

define('MEMCACHED1_HOST','localhost');
define('MEMCACHED1_PORT','11211');

$cache = Proj_Cache::get_instance();

$cache->set('hoge','ほげ');
$cache->set('array',array(1,2,3,4,5,6,7,8,9,10));

var_dump($cache->get('hoge'));
var_dump($cache->get('array'));

Cacheクラス

<?php
class Proj_Cache {

    private static $singleton;
    private static $memcache;

    private function __construct() {
        $this->memcache = new Memcached();
        $this->memcache->addServer(constant('MEMCACHED1_HOST'),constant('MEMCACHED1_PORT'),0.1);
        if(defined('MEMCACHED2_HOST') && defined('MEMCACHED2_PORT')) {
            $this->memcache->addServer(constant('MEMCACHED2_HOST'),constant('MEMCACHED2_PORT'),0.1);
        }
    }

    public static function get_instance() {
        if (!is_object(self::$singleton)) {
            self::$singleton = new Proj_Cache();
        }
        return self::$singleton;
    }

    public function get ($key) {
        // error_log("[ GET CACHE ] key:".$key);
        $var = $this->memcache->get($key);
        return $var;
    }

    public function set ($key,$value,$exp=60) {
        //error_log("[ SET CACHE ] key:".$key." exp:".$exp);
        $var = $this->memcache->set($key,$value,$exp);
        return $var;
    }

    public function remove ($key) {
        //error_log("[ REMOVE CACHE ] key:".$key);
        $this->memcache->delete($key);
    }

    /* @throws RuntimeException */
    public final function _clone() {
        throw new RuntimeException ('Clone isn now allowd against '. get_class($this));
    }

}

いかがでしょうか、ほとんど自分の覚え書きだったりもするのですが・・・
お役に立ちますと幸いです。

関連リンク

2010年3 月6日  |  Written by matsumoto  |  under PHP Yahoo!ブックマークに登録    はてなブックマーク - [PHP] PHP 5.3.2 を早速インストール

こんにちは、松本です。
熱かったり寒かったり 中々でありますが、いかがお過ごしでしょうか。
TwitPepper(ツイットペッパー)の解説も完結しない中、perl案件をこなしつつ、PHPで動的にJSを書いていると中々なカオスであります。


PHP5.3.2のリリース に伴い、早速にアップグレードしまして メモがてらインストールログです。

configure

configureは CentOS5.3 で PHP5.3をインストール(YUM風オプションで) に倣いまして、yum風な設定にしています。

x86_64(64ビット)

以下をコピペしてお使い下さい

./configure –with-apxs2=/usr/sbin/apxs –build=x86_64-redhat-linux-gnu –host=x86_64-redhat-linux-gnu –target=x86_64-redhat-linux-gnu –program-prefix= –prefix=/usr –exec-prefix=/usr –bindir=/usr/bin –sbindir=/usr/sbin –sysconfdir=/etc –datadir=/usr/share –includedir=/usr/include –libdir=/usr/lib64 –libexecdir=/usr/libexec –localstatedir=/var –sharedstatedir=/usr/com –mandir=/usr/share/man –infodir=/usr/share/info –cache-file=../config.cache –with-libdir=lib64 –with-config-file-path=/etc –with-config-file-scan-dir=/etc/php.d –with-pic –with-bz2 –with-curl –with-exec-dir=/usr/bin –with-freetype-dir=/usr –with-png-dir=/usr –enable-gd-native-ttf –with-gettext –with-gmp –with-iconv –with-jpeg-dir=/usr –with-openssl –with-pcre-regex=/usr –with-zlib –with-layout=GNU –enable-exif –enable-ftp –enable-magic-quotes –enable-sockets –enable-sysvsem –enable-sysvshm –enable-sysvmsg –enable-wddx –enable-ucd-snmp-hack –enable-shmop –enable-calendar –with-libxml-dir=/usr –enable-pcntl –enable-mbstring –enable-mbregex –with-gd=shared –enable-bcmath=shared –enable-dba=shared –with-db4=/usr –with-xmlrpc=shared –with-mysql=/var/lib/mysql –with-mysql-sock=/var/lib/mysql/mysql.sock –with-mysqli=shared,/usr/bin/mysql_config –enable-dom=shared –enable-soap=shared –with-xsl=shared,/usr –enable-xmlreader=shared –enable-xmlwriter=shared –with-pdo-mysql=shared,/usr –enable-zip –enable-zend-multibyte –with-mcrypt

i386(32ビット)

以下をコピペしてお使い下さい

./configure –with-apxs2=/usr/sbin/apxs –build=i386-redhat-linux-gnu –host=i386-redhat-linux-gnu –target=i386-redhat-linux-gnu –program-prefix= –prefix=/usr –exec-prefix=/usr –bindir=/usr/bin –sbindir=/usr/sbin –sysconfdir=/etc –datadir=/usr/share –includedir=/usr/include –libdir=/usr/lib –libexecdir=/usr/libexec –localstatedir=/var –sharedstatedir=/usr/com –mandir=/usr/share/man –infodir=/usr/share/info –cache-file=../config.cache –with-libdir=lib –with-config-file-path=/etc –with-config-file-scan-dir=/etc/php.d –with-pic –with-bz2 –with-curl –with-exec-dir=/usr/bin –with-freetype-dir=/usr –with-png-dir=/usr –enable-gd-native-ttf –with-gettext –with-gmp –with-iconv –with-jpeg-dir=/usr –with-openssl –with-pcre-regex=/usr –with-zlib –with-layout=GNU –enable-exif –enable-ftp –enable-magic-quotes –enable-sockets –enable-sysvsem –enable-sysvshm –enable-sysvmsg –enable-wddx –enable-ucd-snmp-hack –enable-shmop –enable-calendar –with-libxml-dir=/usr –enable-pcntl –enable-mbstring –enable-mbregex –with-gd=shared –enable-bcmath=shared –enable-dba=shared –with-db4=/usr –with-xmlrpc=shared –with-mysql=/var/lib/mysql –with-mysql-sock=/var/lib/mysql/mysql.sock –with-mysqli=shared,/usr/bin/mysql_config –enable-dom=shared –enable-soap=shared –with-xsl=shared,/usr –enable-xmlreader=shared –enable-xmlwriter=shared –with-pdo-mysql=shared,/usr –enable-zip –enable-zend-multibyte –with-mcrypt –enable-mbstring

インストール

sudo make install

以下のコマンドでPHPのバージョンが"5.3.2"と表示されればOKです。

$ php -v

PHP 5.3.2 (cli) (built: Mar  6 2010 20:49:38)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies

httpd を再起動してPHPを読み込み直します。

# service httpd restart

eAcceleratorをインストール

eAccelerator をデフォルトで利用しています。
こちらも make し直して、新規にインストールします。

cd /usr/local/src
wget http://bart.eaccelerator.net/source/0.9.6/eaccelerator-0.9.6.tar.bz2
tar jvxf eaccelerator-0.9.6.tar.bz2
cd eaccelerator-0.9.6
phpize
./configure --enable-eaccelerator=shared
export SED=sed
sudo make
sudo make install

php.ini に記述追加

※zend_extension は eaccelerator.so のインストール先を指定します。

※eaccelerator.cache_dir の指定先ディレクトリは予め作成しておきます。

[eaccelerator]
zend_extension="/usr/lib/20090626/eaccelerator.so"
eaccelerator.shm_size = "32"
eaccelerator.cache_dir = "/var/php-cache/eaccelerator"
eaccelerator.enable = "1"
eaccelerator.optimizer = "1"
eaccelerator.check_mtime = "1"
eaccelerator.debug = "0"
eaccelerator.filter = ""
eaccelerator.shm_max = "0"
eaccelerator.shm_ttl = "86400"
eaccelerator.shm_prune_period = "0"
eaccelerator.shm_only = "0"
eaccelerator.compress = "1"
eaccelerator.compress_level = "9"
eaccelerator.keys     = "shm_and_disk"
eaccelerator.sessions = "shm_and_disk"
eaccelerator.content  = "shm_and_disk"

phpのバージョンを再度確認

以下のコマンドでPHPのバージョンが"5.3.2"と表示され、eAccelerator がその下に記述されていればOKです。

$ php -v

PHP 5.3.2 (cli) (built: Mar  6 2010 20:49:38)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
   with eAccelerator v0.9.6, Copyright (c) 2004-2010 eAccelerator, by eAccelerator

eAccelerator を使う環境で VirtualHost をする

eAccelerator を使う環境で VirtualHost をする場合、httpd.conf の 同 VirtualHost 内の セクションに以下を記述します。

name_space 名は、他と重複しなければ何でも良いです

   php_value eaccelerator.name_space "project_name"

メモがてら程度の簡単なログですがお役に立てますと幸いです。

関連リンク

私はチーム・マイナス6%です