メイン

2008年7月24日

daemontoolsでデーモン管理
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

Emacsでbackward-charをC-lに割り当てているbokkoです。「指相撲で相手の指に届かないくらい指が短いので一回も勝ったことがないんです。だからそんな短い指でC-bなんて押してたら指が痛いんです」と言ってもなかなか信じてもらえないのですが、そんな私でも(global-set-key "\C-l" 'backward-char)というelispを評価するだけで快適にプログラミングさせてくれるEmacsが大好きです。


と、タイトルと関係ない話はこれくいらいにしておいて、今日はdaemontoolsのお話です。


daemontools

daemontoolsは異常終了してしまったデーモンプロセスを再起動してくれたり、ログローテートを肩代わりしてくれたりするなど、デーモンの制御や管理、監視を行うプログラムの集まりです。例えば、以下のようなプログラムが含まれています。

  • supervise
    • デーモンの起動、監視
  • svc
    • superviseによって監視されているデーモンの制御
  • svstat
    • 指定したデーモンの状態を出力
  • setuidgid
    • ユーザIDとグループIDを変更
  • multilog
    • デーモンプログラムの出力をロギング

ほかにもいろいろありますが、詳しくは本家のマニュアル(日本語版)を見るとよいでしょう。

インストール

ソースコードがこちらで、SRPMがこちらで配布されています。Linuxディストリビューションによってはapt-getでインストールすることもできますが、その場合は名前がdaemontoolsではなく、svtoolsになっているものがあります(Ubuntuは後者でした)。インストールすると、/commandに各プログラムが配置され、/にserviceというディレクトリが作成されます。daemontoolsでデーモンを管理する場合、この/serviceディレクトリにデーモンの起動スクリプトやログ収集のためのスクリプトを配置します。

デーモンの登録

デーモンを登録するには/service以下に登録したいデーモンの起動スクリプトとログ収集のためのスクリプトを配置します。

$ mkdir /var/daemon
$ mkdir /var/daemon/log
$ mkdir /var/daemon/log/main
$ chmod 1755 /var/daemon
$ chmod +x /var/daemon/run
$ chmod +x /var/daemon/log/run
$ chown hoge:fuga /var/daemon/log/main
$ ln -s /var/daemon /service

/var/daemon/run

#!/bin/sh
exec 2>&1 # エラー出力を標準出力へ
exec setuidgid hoge /usr/bin/daemon

/var/daemon/log/run

#!/bin/sh
exec 2>&1 # エラー出力を標準出力へ
exec setuidgid hoge multilog t ./main

runを実行したプロセス自身をデーモン化しなければいかないので、必ずexecを付けます。また、runで実行するプロセスはフォアグラウンドで起動させなければいけません。

追記:(2008/07/24)

/var/daemon/runの内容が/var/daemon/log/runのものになっており、/var/daemon/runの内容が抜けていたのを修正しました。また、現行のdaemontools-0.76ではstickyビットを立てる必要はありません。ご指摘ありがとうございます。>fumiyasさん

デーモンの起動制御

/service以下のデーモンを起動するにはsvscanを使うとよいでしょう。

$ sh -c 'svscan /service &'

↑を実行すると/serviceディレクトリ以下にあるすべてのデーモンに対してsuperviseによるデーモンの監視が行えます。なんらかの原因でデーモンが終了してしまってもsuperviseが自動的にデーモンを再起動してくれます。ただ、デーモンプログラム自体に問題があって起動できない場合、何度も再起動を試み、ずっとログに起動失敗のメッセージを書き込み続けてしまうので注意しましょう。

OS起動時からdaemontoolsに関連するプログラムを起動しておく場合は、/etc/inittabに以下の記述をしておくとよいでしょう。

SV:123456:respawn:/command/svscanboot      

svcでデーモンに命令を発行する

svcコマンドを使うと指定したデーモンに対して以下のような命令を発行することができます。

$ svc -u /service/daemon # 起動
$ svc -d /service/daemon # 一時停止
$ svc -t /service/daemon # 再起動
               ・
               ・
               ・

svstatで状態確認

svstatを使うと指定したデーモンの状態を確認することができます。

$ svsat /service/daemon # 起動中
/service/daemon: up (pid 8131) 3 seconds
$ svsat /service/daemon # 停止中
/service/daemon: down (pid 8131) 57 seconds, normally up

ただ、たまにdaemontools側から起動しているように見えているだけの時があるので注意しましょう。詳しくは後述します。

ロギング

デーモンプログラムの出力は/var/daemon/log/runで実行しているmultilogによって/var/daemon/log/main以下に保存されます。multilogもdaemontoolsに含まれるツールの一つです。保存されるログはそのままでは見づらいので、同じくdaemontoolsに含まれるtai64nlocalで見やすい形式に変換するのがよいでしょう。

$ tai64nlocal < /service/daemon/log/main/current

デーモンが正常に動作しない場合の対処法

最後にdaemontoolsを使用していて実際に遭遇した問題を紹介します。

デーモンがゾンビプロセス化

なんらかの原因でデーモンが異常終了したり、正常に起動しなかった場合、デーモンプロセスがゾンビ化することがあります。daemontools側から見ると正常に動作しているように見えてしまうので、デーモンが正常に動作しているかpsコマンドなどを使って調べてみましょう。特に、異常終了した後に、そのままでは正常に再起動できないようなプログラムをデーモン化している場合は注意が必要です。

superviseがエラーを吐く

supervise: fatal: unable to acquire daemon/supervise/lock: temporary failure

既に指定したデーモンに対してsupervise(もしくはsvscan)が起動していたり、操作ミスなどによりsupervise(もしくはsvscan)自体が異常終了した場合に上記のエラーメッセージが出ることがあります。
この場合、該当するデーモンに対して起動しているsuperviseを終了し(svscanが起動している場合は先にそっちを終了します)、以下のファイルを削除します。

rm /var/daemon/supervise/lock
rm /var/daemon/log/supervise/lock
rm /var/daemon/log/main/lock

追記:(2008/07/24)

一部誤解を与える表現がありましたので、修正しました。

修正箇所

supervise(もしくはsvscan)自体が異常終了した場合に
↓
操作ミスなどによりsupervise(もしくはsvscan)自体が異常終了した場合に

また、lockファイルを削除するよりもいい方法があるようです(コメント
欄参照)。

ご指摘ありがとうございます>fumiyasさん

フォト蔵におけるdaemontoolsの活用例

フォト蔵では今年の7月頃からHyperEstraierによる全文検索を導入したのですが、このHyperEstraierに含まれるestmasterというサーバプログラムの管理にdaemontoolsを活用しています。使い方の一つとしては、estmasterの死活監視があります。estmasterが停止してしまうと検索自体ができなくなってしまいますが、停止してしまってもdaemontools側で自動的に再起動してくれるわけです。


フォト蔵でのHyperEstraierの活用例に関しては、また後日紹介する予定です。


参考ページ

2008年7月17日

シェルスクリプトでプログラミング
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加


パワプロよりパワポケが好きなbokkoです。でも最近はPSPで遊んでいます。


今回はシェルスクリプトやコマンドラインでよく使うプログラムとその使用例の紹介です。

シェルスクリプトでプログラミング

シェルスクリプトでは既にあるコマンドを組み合わせてプログラムを書きます。しかし、シェルスクリプトは分岐や繰り返しといった制御構造を持ち、関数や変数を扱ったり、四則演算を行うこともできます。

演算

シェルスクリプトで演算を行うにはexprを使います。

add.sh

#!/bin/sh
A=1
B=2
C=`expr ${A} + ${B}`
echo ${C}

↑のadd.shを実行すると、

$ sh test.sh
3
$

と表示されます。↓のようにexprの引数をスペースで区切らず渡してしまうと、単に1+2と表示されてしまいますので、注意しましょう。

#!/bin/sh
A=1
B=2
C=`expr ${A}+${B}`
echo ${C}

浮動小数演算

exprでは整数演算しかできません。シェルスクリプトで浮動小数を扱うにはbcコマンドを使います。

compressibility.sh

#!/bin/sh
SRC_SIZE=100
COMPRESSED_SIZE=15
COMPRESSIBILITY=`echo "${SRC_SIZE} / ${COMPRESSED_SIZE}" | bc -l`
echo ${COMPRESSIBILITY}"%"
$ sh compressibility.sh
6.66666666666666666666%
$

有効桁数を指定するにはscale変数を使います。

#!/bin/sh
SRC_SIZE=100
COMPRESSED_SIZE=15
COMPRESSIBILITY=`echo "scale=2;${SRC_SIZE} / ${COMPRESSED_SIZE}" | bc -l `
echo ${COMPRESSIBILITY}"%"
$ sh compressibility.sh
6.66%
$

分岐

if文の使い方は以下のような感じです。/home/bokko以下にtest.txtが存在していれば、そのファイルが削除されます。

#!/bin/sh
TEST_FILE=/home/bokko/test.txt
if [ -e ${TEST_FILE} ]
then
    rm ${TEST_FILE}
    echo "removed "${TEST_FILE}
else
    echo ${TEST_FILE}" is not existed."
fi

繰り返し

繰り返しにはほかの言語でもよく見られるfor文が使用できますが、書き方はif文と同様、少し独特な感じです。

#!bin/sh
for srcname in `find /home/bokko/programming/c | egrep '.+\.c$'`;do
    case `basename $srcname` in
        a.c | b.c | c.c) ;; # exclude
	*)
	    # C言語のソースファイルに何かする
    esac
done

↑のシェルスクリプトを実行すると、/home/bokko/programinng/c以下にあるC言語のソースファイルに対して「# C言語のソースファイルに何かする」の部分に書かれた処理を実行することができます。「#exlude」のコメントアウトがある行はその名の通り、指定したa.c、b.c、c.cを処理の対象から外しています。

パイプ

シェルスクリプトでプログラムを書いたり、普通にコマンドラインで作業していると、あるプログラムの出力を別のプログラムへの入力として扱いたいことがあります。このような複数のプログラム間でのデータの橋渡しをするのがパイプです。一見複雑そうな問題でも個々の小さいプログラムをパイプで繋ぐことによって簡単に解くことができる場合があります。


例えば、以下のようなファイルから重複した行を取り除きたいとします(いい加減な例ですみません)。

bokko.txt

bokko
bokkko
bokkkko
bokko
bokkko
bokko
bokko
bokko
bokkko
bokkko
bokkko
bokkko
bokkko
bokko
bokkko
bokko
bokko
bokkkko
bokkko
bokkko
bokko
bokko
bokkkko
bokko

これは↓のようにsortとuniqをパイプで繋げるだけでできます。

$ sort bokko.txt | uniq
bokkkko
bokkko
bokko
$

各行がいくつ重複しているか調べるにはuniqの-cオプションを使います。

$ sort bokko.txt | uniq -c
      3 bokkkko
     10 bokkko
     11 bokko
$

非常に簡単な例ではありますが、sortとuniqはテキストデータの集計や解析をする際に非常に役に立ちます。

sed

sedはテキストデータを置換するのによく使われるプログラムです。例えば、先程のbokko.txtの全ての行をbokkoに置換してみましょう。

$ cat bokko.txt | sed -e 's/bok\+o/bokko/g' | uniq
bokko
$

という具合に文字列を正規表現を使って簡単に置換することができます。

awk

awkはsedと同じようにテキストを置換したり、整形するためのプログラムです。sedに比べると正規表現の機能が少し弱いですが、テキストの整形機能のほかに四則演算、連想配列、などの機能が備わっています。

sortとuniqの例に比べるとやや冗長ですが、以下のようなawkスクリプトを用意して重複行を数えることができます。

sort_uniq.awk

BEGIN {
    cnt["bokko"] = 0;
    cnt["bokkko"] = 0;
    cnt["bokkkko"] = 0;
}
/^bokko$/ {
    cnt["bokko"]++;
}
/^bokkko$/ {
    cnt["bokkko"]++;
}
/^bokkkko$/ {
    cnt["bokkkko"]++;
}
END {
    print "bokko:" cnt["bokko"];
    print "bokkko:" cnt["bokkko"];
    print "bokkkko:" cnt["bokkkko"];
}
$ cat bokko.txt | awk -f sort_uniq.awk
bokko:11
bokkko:10
bokkkko:3
$

ほかにも、テキストから必要な部分だけを取り出すといったことも簡単に行える機能があります。

$ du -b test.txt
101     test.txt
$

この出力結果の数字の部分だけ取り出したいなら以下のようにします。

$ du -b test.txt | awk '{ print $1 }'  
101
$

という具合に出力結果の数字部分だけを取り出すのがいとも簡単に行えます。あまり意味はありませんが、以下のようなことも可能です。

$ du -b test.txt | awk '{ print $2 }'  
test.txt
$

つまり、$1、$2という具合に区切り区切りで文字列を取得できるのです。awkではこの区切りをセパレータと呼び、必要に応じて変更することができます。


JavaScriptやCSSの縮小結果を表示する

次はちょっとした応用例を紹介します。

映画生活やフォト蔵ではJavaScriptファイルをjs_min.pyで、また、CSSを自作のPHPスクリプトで縮小化しています。CSSの方は縮小化だけでなく、縮小前のソースファイルの収集処理なども全部PHPで書いているのですが、JavaScriptの方はjs_min.pyをシェルスクリプトから呼び出す形にしています。単に縮小化するのもなんなので、どれくらい縮小化できたのか調べてみようと思い、
縮小率の計算や出力結果のフォーマットもやりました。以下はscriptaculous.jsのslider.jsとcheck.js、prototype.jsに対して縮小化スクリプトで実行した時の出力です。

$ js_minify.sh slider.js
minified:                               slider.js  10354bytes ->   7631bytes reduction percentage:26.30%
$ js_minify.sh slider.js check.js  prototype.js
minified:                               slider.js  10354bytes ->   7631bytes reduction percentage:26.30%
minified:                                check.js   3364bytes ->   2448bytes reduction percentage:27.23%
minified:                            prototype.js  96653bytes ->  72255bytes reduction percentage:25.24%
$ 

↑のスクリプトではJavaScriptの縮小前と縮小後のソースファイルから縮小率を計算してその結果を表示しています。ファイルを縮小化させるのにjs_min.pyを使っていますが、残りはシェルスクリプト、sedやawk、bcなどの既存のプログラムだけで実現しています。

以下がそのシェルスクリプトです。引数を指定しない場合はJS_SRC_DIR以下の全てのJavaScriptファイルを縮小化します。

実際に使う場合はJS_SRC_DIRとJS_MINIFIED_DIRのディレクトリ構成が同じである必要があります。また、ファイルパスの長さやファイルサイズに合わせてprintfのフォーマットを調整するのがよいでしょう。

#!/bin/sh
HOME_DIR=/home/${USER}/jsdir # 任意のパラメータ
JS_SRC_DIR=${HOME_DIR}/jssrc # 任意のパラメータ
JS_MINIFIED_DIR=${HOME_DIR}/minifiedjs # 任意のパラメータ
JSMIN_DIR=/home/${USER}/bin # 任意のパラメータ
TOTAL_SRC_SIZE=0
TOTAL_MINIFIED_SIZE=0
js_minimize () {
    SRC_PATH=`echo ${JS_SRC_DIR}/$1 | sed -e 's/\.\///g'`
    MINIFIED_PATH=`echo ${JS_MINIFIED_DIR}/$1 | sed -e 's/\.\///g'`
    SRC_SIZE=`du -b ${JS_SRC_DIR}/$1 | awk '{ print $1;}'`
    python ${JSMIN_DIR}/jsmin.py < ${SRC_PATH} > ${MINIFIED_PATH}
    MINIFIED_SIZE=`du -b ${JS_MINIFIED_DIR}/$1 | awk '{ print $1;}'`
    REDUCTION_PERCENTAGE=`echo "(1.0 - ${MINIFIED_SIZE} / ${SRC_SIZE}) * 100" | bc -l`
    REDUCTION_PERCENTAGE=`echo ${REDUCTION_PERCENTAGE} | sed -e 's/^\(-\?\)\./\10./g'`
    echo $1 ${SRC_SIZE} ${MINIFIED_SIZE} ${REDUCTION_PERCENTAGE} | sed -e 's/\.\///g' | awk '{
         printf("minified:%s %dbytes -> %dbytes reduction percentage:%5.2f%%\n", $1, $2, $3, $4);
	 }'
    TOTAL_SRC_SIZE=`expr ${TOTAL_SRC_SIZE} + ${SRC_SIZE}`
    TOTAL_MINIFIED_SIZE=`expr ${TOTAL_MINIFIED_SIZE} + ${MINIFIED_SIZE}`
}
cd ${JS_SRC_DIR}
if [ "$1" != "" ]
then
    while [ "$1" != "" ];do
	if [ -e ${JS_SRC_DIR}/$1 ]
	then
	    js_minimize $1
	else
	    echo ${JS_SRC_DIR}/$1" does not exist."
	fi
	shift 1
    done
else
    for srcname in `find . | egrep '.+\.js$'`;do
	case `basename $srcname` in
            exclude1.js | exclude2.js) ;; # exclude
	    *)
		js_minimize $srcname
	esac
    done
    TOTAL_REDUCTION_PERCENTAGE=`echo "(1.0 - ${TOTAL_MINIFIED_SIZE} / ${TOTAL_SRC_SIZE}) * 100" | bc -l`
    echo ${TOTAL_SRC_SIZE} ${TOTAL_MINIFIED_SIZE} ${TOTAL_REDUCTION_PERCENTAGE} \
	| awk '{ printf("\ntotal     : %dbytes -> %dbytes reduction percentage:%5.2f%%\n", $1, $2, $3); }'
    echo | awk '{ printf("\nAll JavaScript files minified.\n\n"); }'
fi

参考までに

縮小化スクリプトによってフォト蔵ではCSSを約15%、JavaScriptを約30%縮小化することができました。中にはファイルサイズが半分以下になったものもいくつかあります。

縮小化自体は既存のスクリプトを使えば簡単にできるので、少しでもサイトのパフォーマンスを上げたいと思っているならば、js_min.pyやYUI Compressorの導入を検討してみるといいかもしれません。

参考文献

シェルスクリプト基本リファレンス
山森 丈範
技術評論社
売り上げランキング: 29058
おすすめ度の平均: 5.0
5 シェルスクリプト本が数ある中で、
5 Linux、Unixユーザならば、一度、読んでおきたい本


sed&awkプログラミング
sed&awkプログラミング
posted with amazlet at 08.07.17
デール ドゥラティ アーノルド ロビンス
オライリー・ジャパン
売り上げランキング: 86344
おすすめ度の平均: 4.5
5 0から書いたことがありません。
5 正規表現をマスターしましょう
4 Sed&awkプログラミング

2008年7月 9日

脆弱性検知ツールratproxyをCygwin上で動かしてみました
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは!やまもと@テスト番長です。

先日Googleからプロキシ型の脆弱性発見ツール「ratproxy」が公開されました。
これは触らないと!ということでCygwin上で動かしてみました。

ratproxy
http://code.google.com/p/ratproxy/


続きを読む "脆弱性検知ツールratproxyをCygwin上で動かしてみました" »

2008年7月 3日

続・Emacsを自分で拡張するためのTips
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加


今年の春頃からトリプルディスプレイで仕事しているbokkoです。なんだか同僚の視線が気になりますが、あえて空気を読まないことにしています。


前に「EmacsLispを自分で拡張する際のTips」という記事を書きましたが、今回はその続きです。

EmacsLispは難しい?

EmacsLisp(以下、elisp)は難しいという意見をたまに耳にしますが、elisp自体はそれほど難しいものではありません。ただ、関数名がバラバラでややこしかったり、マニュアルが巨大でどこを見ていいのかわからず、目的のことをするための関数が見つからない、といったようにユーザが難しいと感じるのはelispという言語そのものではなく、環境(OS、ウインドウ、バッファなど)とのインタフェースにあるため、結果的にEmacsLispは難しいと感じてしまうことが多いようです。
実際、elispでプログラミングしていて感じるのはウインドウやバッファなどのオブジェクトを操作するのにどうやったらいいのかわからなくて途方に暮れるというものです。例えば、

  • バッファを切り替えるにはどうすればいいか?
  • 外部プロセスを起動する方法がわからない
  • find-fileとかみたいに補完入力をしたいんだけどやりかたがわからない

というようなものがあります。調べ方やコツがわかってくれば割とすんなりいくことも多いのですが、慣れるまでは苦労すると思います。(僕もそうでした)

非対話的に引数で指定したファイルを開く

elispの拡張を書いていると、C-x C-fやM-x find-fileのように対話的にファイルを指定して開くのではなく、プログラム中で引数に指定したパスのファイルを開きたい場合があります。
それで以下のような処理を書いたとします。

(find-file-noselect "/home/bokko/test.txt")

名前からして↑の処理を実行するとファイルの内容が現在開いているバッファに表示されそうですが、実際にはそうなりません。この処理を実行すると、指定したファイルのバッファオブジェクトが返ってきます。目的のことをやるには今開いているバッファの内容をこのバッファに切り替える必要があります。バッファの切り替えにはswitch-to-buffer関数を使います。

(switch-to-buffer (find-file-noselect "/home/bokko/test.txt"))
;; もしくは
(switch-to-buffer (buffer-name (find-file-noselect "/home/bokko/test.txt")))

バッファからファイルに関する情報を取得する

現在開いているバッファのオブジェクトを取得するにはwindow-buffer関数を使います。

(window-buffer)

このオブジェクトからファイルの名前を取得するには、

(buffer-name (window-buffer))

とします。また、フルパスで取得するにはexpand-file-nameを使います。

(expand-file-name (buffer-name (window-buffer)))

外部プロセスを起動する

Emacsでは外部のプロセスを起動させることができます。既にあるシェルスクリプトなどを使いたい場合はstart-processのような関数を使うといいでしょう。映画生活では僕が去年ちょこちょこっと作ったCSSを縮小化するスクリプトを使っているのですが、僕の環境ではこれをEmacsから呼び出せるように↓のelispを読み込んでいます。

(defun compress-css ()
  "compress css"
  (interactive)
  (start-process "compress-css"
         "*compress-css*"
         "css-compressor")
  (message "all css file compressed."))

最後のcss-compressorが実行ファイル名です。ここではファイル名だけですが、実際にはパスの通った場所に置くかフルパスで指定します。また、この場合、実行結果は*compress-css*というバッファに吐き出されます。

補完入力をできるようにする

C-xC-f(find-file)でファイルを開くとき、Emacsでは補完入力ができます。去年ぐらいの頃にこれをどうやってやるのかわからず、結構悩んだ時期があったのですが、実は専用の関数が用意されているため、割と簡単にできます。以下のelispを実行すると、ミニバッファ内で補完入力ができるようになります。

(defvar tags-file-list '(
                 ("eigaseikatu" . "/home/bokko/eigaseikatu")
                 ("photozou" . "/home/bokko/photozou")
                 ))
(setq file (completing-read
   "Tags Key: "
   tags-file-list nil t "")) 

タブキーを押すと「eigaseikatu」と「photozou」が候補リストに表示されるようになり、入力した方の値がfileという変数に代入されます。前回紹介したvisit-tags-table-key.elではこの補完入力を行っていなかったため、毎回自分で正確に入力する必要がありましたが、今は上記のようにcompleting-readを使って補完入力をするようにしました。

elispで使う正規表現

elispで正規表現を扱う際は注意が必要です。というより、elispに限らず、プログラム中の正規表現を別言語に直接持って行く際には注意が必要です。というのもプログラミング言語で正規表現を扱う場合、その言語が正規表現をリテラルとして扱うのか文字列として扱うのか考慮する必要があります。特に文字列の場合はなにかと面倒です。elispでバックスラッシュにマッチさせるには、

\\\\

と書く必要があります。これはelispでは正規表現が文字列として扱われるので、余分にエスケープ処理が必要となるためです。sedやawk、もしくはPerlなどで正規表現を使ったプログラムを書いたことのある人には奇妙に思うかも知れませんが、elispやJavaのように正規表現をリテラルではなく、文字列として扱う言語ではこのようにエスケープを多用する必要があります。

デバッグ

elispのデバッグをする際はedebug-defunを使うのがいいでしょう。printfデバッグみたいなこともできますが、gdbみたいにステップ実行しながらデバッグすることができるので非常に便利です。
edebug-defunを使うには以下のelispを評価します。

(setq debug-on-error t)

あとはデバッグしたい関数の末尾でM-x edebug-defunと実行した後、その関数を実行すると、ステップ実行ができるようになります。スペースキーを押す度にミニバッファにelispの評価結果が表示されるので、実際にどのように動作しているのか把握しやすくなります。

目当ての関数を探し出す

先述したようにelispにはものすごくたくさんの関数があり、目的の関数を探し出すのが大変なのですが、実はelispで使える変数や関数はhelp-for-help関数(C-h)を使ってEmacs上で参照できるので、これである程度その大変さを和らげることができます。この関数を使うとUNIXのシステムコールをmanコマンドで調べるのと同じような感覚で関数の使い方について調べることができます。

ちょっとした応用

ここからは私が普段使っているelispのプログラムをちょこっと紹介します。

別のバッファで同じファイルを開く

プログラムを編集していると、同じファイルを複数のバッファから編集したくなるときがあります。例えば、同じファイル内にBという関数を呼び出しているAという関数があって、その両方を編集する必要があるような状況です。単純に同じファイルを同じバッファで開いてしまっても意味はないので、ウインドウを縦に分割して、2つのバッファに同じファイルの内容を表示してみます。

(defun open-same-file ()
  (interactive)
  (let ((same-file (find-file-noselect (buffer-name (window-buffer)))))
    (progn (split-window-horizontally)
    (other-window 1)
    (switch-to-buffer same-file))))

↑の関数を実行すると、C-x 3に割り当てられている関数であるsplit-window-horizontallyによってウインドウが縦に分割され、分割してできた新しい方に元々編集していたバッファの内容が表示されます。僕の環境では、以下のようにelscreen上の別のスクリーンで開くようにしています。

(defun elscreen-open-same-file ()
  (interactive)
  (let ((same-file (find-file-noselect (buffer-name (window-buffer)))))
    (elscreen-create)
    (switch-to-buffer same-file)))

C言語のヘッダファイルとソースファイルを関連づける

C言語でプログラムを書く際、関数の宣言と定義を分離するため、ソースファイル(.c)とは別にヘッダファイル(.h)を書くのが一般的です。そのため、ソースファイルとヘッダファイルとの間を行ったり来たリすることがあります。ずっと面倒だと思っていたので、以下のような関数を呼び出すだけで対応するファイルを開けるようにしました。

(defun c-open-relational-file (how-open-type)
  (interactive "nOpen-Type: ")
  (defun get-opened-file-name-prefix (file-name)
    (string-match "^\\([^.]+\\)\\.[^.]+$" file-name)
    (match-string 1 file-name))
  (defun get-ext-type (file-name)
    (string-match "\\.\\([^.]+\\)$" file-name)
    (match-string 1 file-name))
  (defun get-opening-file-name (file-name-prefix ext-list)
    (let ((opening-file-name (concat file-name-prefix "." (car ext-list))))
      (cond ((null (car ext-list))             nil)
            ((file-exists-p opening-file-name) opening-file-name)
            (t                                 (get-opening-file-name file-name-prefix
                                                                      (cdr ext-list))))))
  (let* ((ext-map '(
                    ("h" . ("c" "cpp" "cxx"))
                    ("c" . ("h" "s"))
                    ("s" . ("c"))
                    ("cpp" . ("hpp" "h"))
                    ))
         (opened-file-name (buffer-file-name (window-buffer)))
         (opened-file-name-prefix (get-opened-file-name-prefix opened-file-name))
         (opened-file-ext-type (get-ext-type opened-file-name))
         (opening-file-ext-type-list (cdr (assoc opened-file-ext-type ext-map)))
         (opening-file-name (get-opening-file-name opened-file-name-prefix
                                                   opening-file-ext-type-list))
         (opening-file-buffer (find-file-noselect opening-file-name)))
    (cond ((= how-open-type 1) (elscreen-switch-or-create opening-file-buffer))
          ((= how-open-type 2) (progn (split-window-horizontally)
                                      (other-window 1)
                                      (switch-to-buffer opening-file-buffer)))
          (t                   (message "Illegal Type")))))

この関数を実行すると、今編集しているファイルがtest.cというファイルの場合、test.hを別のバッファで開くことができます。最初にどう開くのかを数字で指定して、1の場合はelscreenを使って別のバッファで開くようにし、2の場合はウインドウを縦に分割して新しくできたバッファに対応するファイルを読み込みます。また、ソースファイルとヘッダファイルだけでなく、アセンブリコードのファイル(.s)も開けるようにしています(ただし、ヘッダファイルがある場合はそちらを優先します)。
上記のelscreen-switch-or-createは前回紹介したswitch-to-elscreen-createと同じものです。

elファイルを再帰的にバイトコンパイルする

elispのプログラムはそのままでもEmacsが読み込むことができますが、バイトコードにコンパイルすることによって高速化することができます。大きな拡張だと、Makefileが用意されていて、(configure→)make→make installでインストールできるようになっているものもありますが、そうでないものも存在するため、たまに以下のelispを使って複数のelファイルを一気にコンパイルしています。

(defun my-byte-compile-directory ()
  (interactive)
  (defun byte-compile-directories (dir)
    (if (file-directory-p dir)
        (byte-compile-directory-r (mapcar (function (lambda (f) (concat dir "/" f)))
                                          (directory-files dir)))))
  (defun byte-compile-directory-r (file-list)
    (cond ((null (car file-list))
           nil)
          ((and (file-directory-p (car file-list))
                (not (string-match "/\.\.?$" (car file-list))))
           (byte-compile-directories (car file-list))
           (if (not (null (cdr file-list)))
               (progn
                 (byte-compile-directories (cadr file-list))
                 (byte-compile-directory-r (cdr file-list)))))
          ((string-match "\.el$" (car file-list))
           (progn
             (byte-compile-file (car file-list))
             (byte-compile-directory-r (cdr file-list))))
          (t
           (if (not (null (cdr file-list)))
               (byte-compile-directory-r (cdr file-list))))))
  (byte-compile-directories (replace-regexp-in-string "/$" "" default-directory)))

依存関係とかが複雑でないなら、バイトコンパイルしたい拡張のディレクトリに移動して↑の関数を実行するだけで全てのelファイルをバイトコンパイルしてelcファイルを生成することができます。

追記:(2008/7/3 18:20)

my-byte-compile-directory関数にバグがあったので修正しました。バグの内容ですが、再帰的にバイトコンパイルすると書いてあるのにサブディレクトリのelファイルをコンパイルできるようになっていませんでしたorz。上記の修正版ではちゃんとサブディレクトリのelファイルもバイトコンパイルされます。

参考文献

やさしいEmacs‐Lisp講座

おまけ

display3.png

2008年6月12日

Google Analyticsで特定のリンクのクリック数を調べる
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

yamaokaです。

Webサイトを運営していると、同じページに複数の箇所からリンクが貼られている場合に、特定のリンクからのアクセスがどれぐらいあるかを調べたいことがあります。

もちろん、異なるURLで訪問者にアクセスしてもらえばトラッキングできるので、適当なクエリーストリングをリンクのURLに付加すれば調べることができるようになります。

<a href="/foo?bar">baz</a>
ただし、その場合permalink(固定されたURL)ではなくなってしまいますね。

写真でつながる共有サイト「フォト蔵」では、アクセス解析にGoogle Analyticsを利用しています。Google Analyticsを利用している場合、次のようにして特定のリンクからのアクセスを違う名前で扱うことができるようになります。

<a href="/foo" onclick="pageTracker._trackPageview('/foo?bar')">baz</a>
詳細な情報については、Googleによる「_trackPageviewとは何ですか。どのような場合に使用しますか。」を参照してください。この方法なら、URLは元の状態のままpermalinkを保つことができます。

このリンクからのアクセス数をGoogle Analyticsで調べるには、「/foo?bar」というURLにどれだけアクセスされているかを検索すればOKです。簡単ですね。単純なページ遷移なら「ナビゲーションサマリー」で確認することができますが、特定のリンクをターゲットに調査したい場合にはこの方法が便利です。

Google Analyticsは本格的に使おうとするとかなり高機能なので、ヘルプを見たりあちこちのページを開いたりして、いろいろ試してみるといいと思います。「セッション」「コンバージョン」など、Google Analyticsで用いられている用語の定義も一度確認しておくことをオススメします。

2008年6月 3日

テスト担当者のモチベーション #2
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは! やまもと@テスト番長です。
今年も梅雨の季節になりました。これから毎日じめじめするのかと思うと、どうしてもテンションが下がりがちになりますね。前回モチベーションのお話をしたのですが、その後で良い記事を見つけたのでご紹介したいと思います。

Out of the Rut
By Michael Bolton

テストに飽きてしまったらどうしたらよいか?という問題について筆者の経験から色々とアドバイスをしてくれています。詳細は記事を読んでいただくとして、見出しだけちょっとまとめてみましょう。

担当箇所を取り替えてみる


担当箇所を他のメンバーと交換すると、より上手くいくようになるかもしれません。

変化をつける

いったん手を止めて、今と違うアプローチの仕方を考えてみましょう。

他の人々とコミュニケーションする

プログラマとチャットしたり、ハマる箇所についてユーザーと話したり、他のテスターとペアでテストしてみましょう。リフレッシュできます。

他の何かに注目する

一つのことに注目しすぎて煮詰まるのは良くありません。

自動化する

単調な繰り返しのタスクはなるべく自動化してしまいましょう。

費用対効果を考える

やらなくていい作業だからやる気が出ないのかもしれません。重要でないものにコストをかけるのは、クライアントも望まないでしょう。

作業を確認する

自分の作業が有益なものかクライアントやマネージャーと話し合いましょう。

ちゃんと休憩する

散歩に出たり、雑誌を読んだり、自転車に乗ったり、コーヒーを飲んだり、お菓子を食べたり、お使いをしたり、シャワーを浴びたりして、気分を変えましょう。


記事の最後に、「テストがつまらないのはそれが上手くいっていないときだけで
重要な情報を探したり、何かを学べているときは面白いものだ」と述べられています。

常に前向きでいられる工夫を忘れないようにしたいものですね。

2008年5月12日

Mercurialでバージョン管理
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

stoplightで最大化したターミナル上でzshとscreenとEmacsを立ち上げ、 明朝体フォントでプログラミングするbokkoです。

今回はバージョン管理システムの1つであるMercurialについて紹介します。

ウノウではSubversionとTracを組み合わせて開発を行っていますが、 僕個人では今年の春ぐらいからEmacsやzsh、screenなどの各種設定ファイルをMercurialでバージョン管理しています。

Mercurialとは?

Mercurialは分散型のバージョン管理システムです。 これに対して、CVSやSubversion(以下SVN)は集中型のバージョン管理システムにあたります。 分散型と聞くと難しそうなイメージがわくかもしれませんが、 CVSやSVNに比べてると、より手軽にバージョン管理を行うことができるというのが、 Mercurialに対する僕の印象です。というのもCVSやSVNでは、まず単一のリポジトリを作成した後、 バージョン管理したいファイルをリポジトリにimportして、コピーをチェックアウトし、 コピーを編集してからその差分をリポジトリにコミットします。 これに対して、Mercurialではバージョン管理の対象そのものがリポジトリになります。 このため、単に1つのマシン上で編集の履歴を保持したりするだけの場合、 集中型よりも手軽に扱うことができます。もちろん、複数のマシンでファイルの同期を取るといったことも可能です。

続きを読む "Mercurialでバージョン管理" »

2008年4月30日

Javaコードのバグを発見するFindBugs
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。中村です。

Javaコードをコンパイルしたクラスファイルからバグを見つけ出すFindBugsというツールを使ってみました。

FindBugsは単体で動作しますが、IDEのプラグインとしても提供されています。今回はEclipseのプラグインを試してみました。

プラグインの導入

SourceForgeのダウンロードページからedu.umd.cs.findbugs.plugin.eclipse_1.3.3.20080401.zipを取得して 解凍、Eclipseディレクトリのpluginsに置くだけといういつも通りな感じです。

使い方

プロジェクトのプロパティから通知する問題を選択したりと色々設定が出来ますが、単純に使うにはJavaプロジェクトのところで右クリック -> Find Bugs -> Find Bugsをクリックすると プロジェクトを調べて問題箇所にマーカーを付けてくれます。

使ってみる

早速無茶なコードを書いてみました。

FindBugs
FindBugs posted by (C)フォト蔵

すると、このようにバッチリと問題点が指摘されました。実行してNullPointerExceptionの発生箇所の行数を見るよりはこちらの方が一目瞭然で判りやすいと思います。

次に書いてはいけないコードの類をやってみました。

package net.unoh.findbugstest;

public class FindBugsTest {

    public static void main(String[] args) {
        Thread thread = new Thread();
        thread.run();
    }

}

すると次のように問題点が指摘されました。

[Ru] スレッドの中で run を実行しています。(本当は startを代わりに呼び出すべきではないのですか?)。 [RU_INVOKE_RUN]

このメソッドは、オブジェクトのrun()メソッドを明示的に呼び出しています。一般にRunnableを実装したクラスは、新しいスレッドがrun()メソッドを呼び出す事を期待しており、この場合、Thread.start()を呼び出すのが正しいやり方です。

こうした説明文がしっかりと表示される(しかも日本語で)点からすると、教育目的で使うのも良いかもしれません。

他に同じようなことをするものにLint4jなどがあるようなので、今度比較をしてみたいなと思います。

2008年4月22日

CSSによるデザインワークと相性のよいHTMLって?
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

yamazakiです。最近だいぶあたたかくなってきましたね。おかげで日中眠くて仕方ないわけですがいかがお過ごしでしょうか。

ウノウに入る以前も含めてそれなりに長いことHTMLとCSSを書いてきたわけですが、今回は試みに、「だいたいこういうところに気を使われたHTMLだと、CSSでのデザイン適用やレイアウトがやりやすいな」というこれまでの経験則を簡単ですがまとめてみたいと思います。

まあ、このあたりはCSS書く人とHTML書く人の間でちゃんとルールを決めておけばいいだけの話なので、そもそも何の役に立つのか疑問といえば疑問ですが(笑
たとえばユーザがCSSを書いてスキンを作れるようなサービスを作る際、どういったHTMLにするかを決める、みたいな時には少し参考になる、かもしれません。

ID、クラスを適切に割り振って、要素がCSS側から一意に特定できるように

たとえばグローバルナビゲーションとカテゴリー別のナビゲーションが同じulで書かれているとして、どちらがどちらかCSSで特定できないと、別の見た目にしようとしても難しいです。

<ul>
<li>グローバル</li>
<li>グローバル</li>
</ul>

<ul>
<li>カテゴリ内</li>
<li>カテゴリ内</li>
</ul>

なので可能な限り「表示される内容に共通項のない部分」にはそれぞれID割り振るなどして特定できるようにします。

<ul id="gNavi">
<li>グローバル</li>
<li>グローバル</li>
</ul>

<ul id="catNavi">
<li>カテゴリ内</li>
<li>カテゴリ内</li>
</ul>

さらに、「同じ『ナビゲーション』なので」ということで、同じクラスを振っておくと、似たような見た目にしたい場合に便利。

<div id="gNavi" class="navigation">
<ul>
<li>グローバル</li>
<li>グローバル</li>
</ul>
</div>
<div id="catNavi" class="navigation">
<ul>
<li>カテゴリ内</li>
<li>カテゴリ内</li>
</ul>
</div>

もちろん、たとえば「ページ内にH1タグは一つしかない」というポリシーが全ページできちんと貫かれていれば、H1タグに余計なIDをつけたりしなくても問題ないと思います。
大事なのは「ちゃんと別々のものだと区別できること」です。

同じようなものには同じクラスを割り振る

IDはその名の通り、ページ内に唯一のものに割り振っておくべきものです。一方で「クラス」は、同じようなものを「これとこれは同じようなものですよ」ということを表す場合に使います。
似たものはやはり似たような見た目にしたい場合が多いので、似たような要素にはできるだけ同じクラスが割り振られているほうが好ましいと思います。
逆に、たとえば同じような内容なんだけどちょっとだけ違うものに、全く別のクラスが指定されていると色々と面倒です。
たとえば、ブログの新着記事のリストと人気記事のリストがあったとして、

<ul class="recentPostList">
<li>記事1</li>
<li>記事2</li>
</ul>

<ul class="popularPostList">
<li>記事i</li>
<li>記事ii</li>
</ul>

としてしまうと、二つのリストに同じ見た目を指定したいときに、

ul.recentPostList,
ul.popularPostList{}

といちいち書かなくてはいけなくなります。※まあ2つくらいだったらいいのですが、これが多くなってくるとだいぶ残念な感じになっていきます。
こういう場合は、クラスはスペースで区切って複数指定できますので、たとえば

<ul class="postList recent">
<li>記事1</li>
<li>記事2</li>
</ul>

<ul class="postList popular">
<li>記事i</li>
<li>記事ii</li>
</ul>

とすると、CSSでは、共通のスタイルを「ul.postList」で指定し、それぞれちょっと別の見た目にしたければ「ul.recent/ul.popular」に指定、という形でスマートに書きやすくなりますし、メンテナンス性もよくなると思います。

tableのtrごとに「odd」「even」

テーブルは特に大きくなると見にくくなりがちです。
1行おきに色などを変えられるようにする意味で、テーブルの行ごとにクラスをなにかしら交互に指定してあると、スタイル割り当てがしやすくなります。
同じようにリスト系要素(li)など繰り返される要素にも割り当ててあると便利かも。

リスト系要素(ul,ol,dl)の最初と最後が特定できるようにしておく

:first-childなどの擬似要素がすべてのブラウザでちゃんと使えればこんなものはあまり必要ないのですが、IE6などの対応状況を考えると現実問題としてまだまだ使えません。
ので、リストなどの同じ要素の連続するものの場合は、できるだけ最初と最後に「これが最初の要素」「これが最後の要素」だということがわかるようにしておきます。

<ul>
<li class="first">xxxx</li>
<li>xxxx</li>
<li>xxxxx</li>
<li class="last">xxxx</li>
</ul>

なんでこういうことが必要になるかというと、

  • 最初の要素だけマージンを狭く/広くしたい
  • 最後の要素だけマージンを狭く/広くしたい
  • 最初の要素のところにだけ背景画像指定したい
  • インライン化して横並びのメニューにして、間に線を入れたいんだけど全部のli要素に線を割り当てると一本余計になるので消したい

などなど、最初/最後の要素はちょっとだけ違うスタイルにしたいことが結構よくあるからです。

h1~h6タグ直下には<span>か<a>タグが必ず入るようにしておく

「見出し」はやはり「画像に差し替えたい」とか、「特別な装飾を施したい」とかいったことがよくあります。
また、文字の書かれてる部分だけに装飾したい場合や、margin/paddingのコントロールの都合で子インライン要素があるほうが調整しやすくなります。
といったわけで、直下に<span>もしくは<a>タグなどのインライン要素が入るようにしてあると、CSSでのデザインの幅がだいぶ広がると思います。

<h1>hogehoge</h1>

ではなく

<h1><span>hogehoge</span></h1>

その他にもCSSの猛者の皆様的に「こうなっていると嬉しい」という例あると思いますので、もし何かあればぜひ教えてください!

2008年4月 9日

テスト担当者のモチベーション
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは!やまもと@テスト番長です。

一般人に向かって自己紹介するとき、
「一応サラリーマンです。WEBサイト作ったりとかしてます。」「専門はテストです。」というと
「出来栄えをチェックする人だから、エライ人なんですねー」
と若干良い方に誤解されがちですが、
同業者に「専門はテストです。」というと「あー、大変っすよねー」と必ず同情されます。

テストというのはどうもモチベーションが上がりにくいお仕事のようです。
今回は来るべき五月病シーズンに向けて、特に新人に近い立場の方がモチベーションを失わずに居られる方法を幾つか考えてみましょう。

テスト担当を押し付けられたとき

新人を安易にテスト業務に割り当てるケースがあります。 新人はまだ経験と信頼性が足りない故に他の作業で使いづらく、そうなりがちです。

もしプログラミングの方に興味があるなら、そういう意向をアピールしておくべきです。
いつ頃プログラミングをさせてもらえるか、見通しを上司と話し合っておきましょう。

その上で今は経験を積むためだと割り切ってしまうのがいいでしょう。
テストのやり方を覚えるのも相当大事なことですし、テストしていても仕様書もコードも見られるし、決して退屈はしないハズ。
#いきなりプロジェクトが炎上してて退屈どころじゃないかもしれませんが。

要領良くこなすとスッキリ。

テストケースの出来具合によっては、まとめてチェック出来る項目が大量にあったりします。 要領良くテストを実施する方法を常に考えましょう。単純作業はなるべく排除しましょう。 やりがいのあるテスト項目に己の時間と精神力を集中させましょう。

攻めの姿勢に出る

用意されたテストケースをただ大量にこなしているだけだと飽きることもあるでしょうが、ここは一つ攻めの姿勢でいきましょう。 テストケースは抜けがあったりするものです。普通あります。 実施しながら穴を探しましょう。穴を見つけたらフィードバックしましょう。 レアなバグを見つけたら、周りの人に見せて褒めてもらうのがいいですね。 チームから頼りにされるようになりましょう。

スキルアップを考える

同じようなテスト作業ばっかり日々繰り返していると、成長できていないことに気づき、人生このままでいいのか悩みだします。スキルアップを心掛けましょう。 テスト関連の良い書籍や資格制度も出てきていますので、手を伸ばしてみましょう。 仲間がいれば一緒にやるといいですね。

休める時に積極的に休暇を取る

テストに関わる(廻される)人は真面目な人が多いので、休むことを積極的に考えましょう。 神経を使う仕事なのでリフレッシュは重要です。 体を動かしたり、趣味の時間を確保するのもいいですね。

たまにはコーディングする

たまにコーディングすると気分転換になります。 プログラムを(ある程度は)組めないとテストも出来ないので、離れてしまわないようにしましょう。 テスト用のツールなどを作れば仕事も楽になりますよ。

会社でのテスト業務の地位が低くて萎えるなら

テストの重要性を理解してもらえない環境だと、テンションのあがりようがありません。 テストを軽視するような人は所詮素人なので、そんな人達が何を言おうと気にすることはありませんが、心が乾いてしまう前に次の展開を考えましょう。


とにかく、状況に流されて何もしないのは一番よくありませんね。
なんだかテストに限らない気もしてきましたが、よかったら試してみてください。

他にも良い方法があればぜひ教えてください!

2008年4月 4日

gdbの使い方
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

今年の2月にマカーになったbokkoです。どうも僕の使っているフォントがほかの人には見づらいらしく、「そのフォントはねぇよw」と言われたり、外付けのキーボードを使っているせいか、「MacBookの意味なし!」と社内で言われてたりしています。

今日はgdbのお話です。gdbは非常に広く使われているデバッガで、特にC、C++のプログラムをデバッグするのによく使われています。

デバッガの使い方

プログラムをデバッグする際、例えば以下の方法が挙げられます。

1. ソースコードを読む
2. ソースコードに出力関数を仕込む(例えばprintf)
3. ソースコードを書き換えて実行してみる

これで十分な場合もありますが、そうでない場合もあります。これらの方法ではプログラムを実行している最中にこちらからソースコードレベルでのアクションを起こすことが難しいので、例えば、プログラムをある時点で止めて変数の内容を確認するといったことが困難です。2と3でもできないことはないですが、CやC++だと何か別のことをやろうとする度にコンパイルし直す必要があります。そこでデバッガの出番です。デバッガを使えばプログラムを1行ずつ実行しながら変数の内容を確認したり、あるループ内の処理がどう実行されてどのようにそのループを抜けたのか確認するのがやりやすくなります。

続きを読む "gdbの使い方" »

2008年3月25日

Amazonの商品個別ページのURLからASINを取得する
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

isogawaです。

題名のままのコードを書く用事があったので、できたものを晒します。とはいえ、ググれば多くの先達が既にさまざまなコードを書いておられ、目新しいものではないんですが。ついでなんで、大昔の「ISBN=~」を含めて、自分の知ってるURLのパターンを洗いなおしてみました(それでも、まだ他のパターンがありそうな気がしますが…)。

例えば、「The Elements of Style」という書籍の個別商品URLとして思いつくものには、以下のようなパターンがあります(細かいバリエーションは適宜省略)。

スキームがhttpの場合とhttpsの場合、ホストの「www.」の有無、及び日本のサイトについては「.co.jp」と「.jp」の両ドメイン名で、表示内容に違いは見られないようです。

なお、ASIN以降スラッシュに続けて、アソシエイトIDを初めとした各種パラメータが付与されますが、必ずASIN以降であるようなので、ここでは無視します。

というわけでコードですが。以下はJavaScriptですが、やってることはパスをバラして正規表現で評価してるだけなので、他の言語にもサクっと置き換えられるかなと。

続きを読む "Amazonの商品個別ページのURLからASINを取得する" »

2008年3月21日

Apache MPM の基礎をしっかりと理解しよう!
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

naoya です。最近、とうとう花粉症の季節に入ったので、生まれて初めて空気清浄機を購入しました。

さて、今日は Apache HTTP サーバの MPM (Multi Processing Module) について解説したいと思います。普段、ウェブサーバを構築するとき、Apache HTTP サーバを使うことが多いと思いますが、Apache HTTP サーバを使う上で MPM について理解しておくことは大事です。

この MPM ですが、Apache HTTP サーバではリクエストを処理する部分のことをさします。MPM は、Apache HTTP サーバ 2.0 系から採用されています。Apache HTTP サーバの MPM には、次の種類があります。

  • prefork
  • worker
  • perchild
  • winnt

それぞれの MPM について解説します。まずは、一番代表的な prefork MPM について解説します。prefork MPM の特徴は、次のとおりです。

  • Apache HTTP サーバ 1.3 系以前のモデル
  • 1 つのリクエストに対して、1 プロセスを割り当てる
  • 負荷に応じて、子プロセスを増減させる

prefork MPM は、次に解説する worker MPM と同じくらいよく使われる MPM です。PHP で使うときは、PHPで書かれたwebサービスを高速化する にもあるとおり、prefork MPM を使う必要があります。PHP を使うとき、worker MPM を使うことは推奨されていないので注意してください。

prefork MPM のデフォルト設定は、次のようになっています。prefork MPM では、ServerLimit と MaxClients は同じ値を設定します。



StartServers 8
MinSpareServers 5
MaxSpareServers 20
ServerLimit 256
MaxClients 256
MaxRequestsPerChild 4000

関連するディレクティブの説明は、次のとおりです。

  • StartServers: 最初に起動する子プロセスの数
  • MinSpareServers: 待機する子プロセスの最小数
  • MaxSpareServers: 待機する子プロセスの最大数
  • MaxClients: 生成する子プロセスの最大数
  • MaxRequestsPerChild: それぞれの子プロセスが扱うリクエスト数の制限数

フォト蔵 のウェブサーバも prefork MPM を使っています。フォト蔵のウェブサーバの設定は、次のとおりです。



StartServers 8
MinSpareServers 5
MaxSpareServers 30
ServerLimit 300
MaxClients 300
MaxRequestsPerChild 50

MaxRequestsPerChild の値が小さいのが特徴だと思います。MaxRequestsPerChild を小さくしている理由は、PHP を使っているとかなりメモリを消費するため、頻繁に子プロセスを生成しなおしたいためです。

prefork MPM を使う場合、大量のアクセスを捌きたい場合には MaxClients の値を増やせばいいのですが、あまり増やしすぎてしまうとメモリ不足などになることが多いので、MaxClients を増やすときは ab(Apache Bench) コマンドなどを使って負荷テストを粉って、どのくらいまで設定できるのか見極める必要があります。


次に workder MPM について解説します。worker MPM の特徴は、次のとおりです。

  • スレッド対応型
  • リクエスト処理は、スレッドが対応する
  • 負荷に応じて、子プロセス数を増減させる
  • 1 子プロセスあたりのスレッド数は固定になっている

worker MPM は、子プロセス数を増減させるというところは prefork MPM と似ていますが、リクエスト処理をスレッドが対応するというところが大きく違っています。worker MPM は子プロセスを生成したとき、リクエスト処理をするスレッドを最初にすべて生成するので、新しいリクエストがきたとき、prefork MPM のように子プロセスの生成を待つ必要がないので prefork MPM よりパフォーマンスがいいです。(*1)

worker MPM のデフォルト設定は、次のようになっています。



StartServers 2
MaxClients 150
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0

関連するディレクティブの説明は、次のとおりです。

  • StartServers: 最初に起動する子プロセスの数
  • MinSpareThreads: アイドル状態のスレッド最小数
  • MaxSpareThreads: アイドル状態のスレッド最大数
  • MaxClients: 生成する子プロセスの最大数
  • ThreadsPerChild: 子プロセスそれぞれに生成されるスレッド数
  • MaxRequestsPerChild: それぞれの子プロセスが扱うリクエスト数の制限数


次に perchild MPM について解説します。perchild MPM の特徴は、次のとおりです。



  • スレッド対応型

  • リクエスト処理は、スレッドが対応する

  • 負荷に応じて、スレッド数を増減させる

  • 子プロセスの数は固定になっている

perchild MPM は、worker MPM の反対のような特徴をもっており、スレッド数だけが増減する仕組みとなっています。そのため、子プロセスの生成を待つ必要がなく、スレッドも必要なだけになるため、もっともリソースを有効に使うことができる MPM といえます。ただし、prechild MPM は不安定な面もあるという情報があるので、本番環境で使う前には入念なテストが必要です。


最後に winnt MPM について解説します。winnt MPM の特徴は、次のとおりです。



  • スレッド対応型

  • リクエスト処理は、スレッドが対応する

  • Windows NT に特化したスレッドモデル

winnt MPM は、その名の通り Windows NT 向けに特化した MPM です。Windows 上の Apache HTTP サーバでは、winnt MPM がデフォルトで使われます。


というわけで、今回は Apache HTTP サーバの MPM について解説しました。普段から使っているオープンソースでも内部の仕組みを理解しておくと、何かのトラブルに遭遇したときに助けのヒントになることがあるので、仕組みを理解するということを大事にしてきたいと思います。

(*1) 追記:prefork MPM より worker MPM の方がパフォーマンスが高い理由は、リクエスト処理をするために子プロセスを生成するではなくスレッドを使うところにありますが、プロセスとスレッドの生成コストの違いについては別の機会にエントリしたいと思います。

2008年2月12日

モバイルサイトをテストする時の基本的なコツ
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは!山本@テスト番長です。

今回は下記のエントリも参考にしながら、モバイルサイトのテストをする際の基本的なコツを書いてみたいと思います。

A Primer in Testing Mobile Phone Applications


URLは直接入力せずにメールで送る


長いURLをへこへこ入力しているとタイプミスしがちです。
PCからメールでささっと送っておきましょう。

テスト端末にメールアドレスを貼っておく

テスト端末が沢山あると、個々のメールアドレスが何だったかすぐ忘れます。 もちろん操作して表示できますが機種によって迷うことがあるので、テプラで裏に貼っておけばすぐ確認できますね。

パスワードは短めに設定する