VOYAGE GROUP エンジニアブログ

voyagegroup_techのブログ
VOYAGE GROUPエンジニアブログです。

重たい処理を華麗にスルーして、アプリケーションの体感速度をぐっと向上させる方法

こんにちは。adingoでプログラマをしている真幡です。

アプリケーションの評価指標の一つにレスポンス性能があります。どれほど素晴らしいアプリケーションでも、レスポンスを返すまでに時間がかかるシステム(=重いシステム)は敬遠されがちです。今回はGearmanというジョブキュー管理ソフトウェアを使い、ウェブアプリケーションのレスポンス性能を向上させる方法を紹介します。

ジョブキューとは何か

ジョブキューとはジョブをキューで管理するものです。これでは説明になっていませんね。キューとはFIFO(First In First Out)を実現するデータ構造です。キューに登録されたモノは、キューに登録した順に処理されます。ジョブキューにおいては、キューに登録するモノはジョブなので、キューに登録した順にジョブが処理されることになります。ジョブキューに登録するジョブの粒度は大小を問いません。したがって、ユーザから重たいジョブを処理するようにリクエストされたときに、ジョブを逐次的に処理せず、ジョブキューに登録することで「ジョブの受付をしました」とユーザにレスポンスを返すことができます。「リクエストを受け付ける=>リクエストを処理する=>レスポンスを返す」という従来の流れから「リクエストを受け付ける=>(とりあえず)レスポンスを返す=>リクエストを処理する」という流れを実現することができます。レスポンスの早さはユーザ体験の向上につながるので、これはうまいやり方であると言えます。

次の図はジョブキューの全体像です。

ジョブキュー

ジョブサーバーが持つキューに注目してください。ジョブサーバーから出ている矢印を見ると、ジョブをキューの上側から詰めていることが分かります。一方、ジョブキューから出ている矢印を見ると、ジョブをキューの下側から取っていることが分かります。先ほどの「キューに登録した順にジョブが処理される」という説明を図で表現すると、このようになります。

より具体的なケースとして、ブログサービスにおける「ブログ記事の投稿」について考えてみましょう。このブログサービスでは、記事が投稿されると次のような処理を行うとします。

  • 記事内容をデータベースに追加
  • 添付画像を(モバイル用の)サムネイル画像に変換
  • RSSファイルの更新
  • 記事投稿の完了通知をメールで送信
この一連の処理において、添付画像の処理が重たい処理であると仮定します(例えば5秒程度の処理時間がかかるとします)。この場合、とりあえず記事内容をデータベースに追加し、それから先の処理をジョブキューに登録し、先にユーザにレスポンスを返すという実装方法が考えられます。こうすることで、ユーザのブラウザを5秒程度フリーズさせる部分を解消することができます。この代償として、添付画像の反映、RSSファイルの更新、メールの送信が5秒程度遅れることになりますが、サービスの性質を考えると、それほど大きな問題ではないと思います。

ところで、同じようなことをcronを使って実現することも可能です。短い間隔でcronを実行し、その中で添付画像の処理、RSSファイルの更新、メールの送信をすれば同様の結果を得られます。では、cronではなくジョブキューを使うメリットは何でしょうか。後述しますが、ジョブキューではワーカーと呼ばれるプロセスが常駐することで、プロセス起動のコストを削減することができます。cronが起動するプロセスがライブラリなどを大量に読み込む必要がある場合、プロセス起動のコストは高くつきます。そのような場合は、cronで都度プロセスを立ち上げるより、ジョブキューの常駐プロセス(ワーカー)を使う方が適切でしょう。

以上のことをまとめると、次のような処理を行うケースではジョブキューを使うメリットがあると言えます。
  • 即時性を求められない処理
  • プロセス起動のコストが高い処理
ジョブキューを効果的に使うことによって、アプリケーションの体感速度を向上し、システムの負荷を下げることも可能であると納得できたでしょうか。以下では、Gearmanというジョブキュー管理システムの使い方について説明します。

Gearmanのインストール

Gearmanのインストール方法は次の通りです。インストール環境は「MacOSX Snow Leopard」を想定しています。

$ sudo port selfupdate
$ sudo port install gearmand
$ sudo pecl install gearman channel://pecl.php.net/gearman-0.7.0

最後に、php.iniに"extension=gearman.so"を追記してください。

Gearmanの構成要素

Gearmanの構成要素には次の3種類があります。

  • ワーカー(またはコンシューマー): 要求に応答する役割
  • クライアント(またはプロデューサー): 要求を生成する役割
  • ジョブサーバー(またはエージェント): クライアントとワーカーの仲介を行う役割

Gearmanデーモンの起動

gearmand(Gearmanデーモン)を起動します。次のようにコマンドを打ちます(前述のようにGearmanをインストールした場合)。

$ /opt/local/sbin/gearmand -d

"-d"オプションはバックグラウンドで起動するためのオプションです。次のようにgearmandプロセスが残れば問題ありません。

$ ps xu | grep gearmand | grep -v "grep"
y-mahata 1314 0.0 0.0 2435700 320 ?? Ss 1:35PM 0:00.00 /opt/local/sbin/gearmand -d

クライアントとワーカーを動かす

ワーカーとしてworker.phpを用意します。

<?php
// worker.php
// ワーカー(コンシューマー)
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('log', 'my_log');
while ($worker->work());

function my_log($job)
{
sleep(60);
file_put_contents('/tmp/foo/' . $job->workload(), $job->workload());
}

ワーカーをバックグラウンドで動作させるため、実行するときには"&"を付けます。

$ php worker.php &

worker.phpについて説明します。これは、GearmanWorkerオブジェクトを生成し、addServer関数でジョブサーバーを追加します。引数なしでaddServer関数を呼ぶと127.0.0.1がジョブサーバーとして追加されます。その後、ワーカーとして提供する関数をaddFunction関数で追加します。この例では、60秒のスリープの後に/tmp/foo以下にログを吐き出すmy_log関数を"log"という名前で(クライアントに)提供します。

次に、クライアントとしてclient.phpを用意します。

<?php
// client.php
// クライアント(プロデューサー)
$client = new GearmanClient();
$client->addServer();
for ($i = 0; $i < 10; $i++) {
// $client->do('log', $i);
$client->doBackground('log', $i);
}

こちらは、何も考えずに実行することができます。

$ php client.php

client.phpについて説明します。これは、GearmanClientオブジェクトを生成し、addServer関数でジョブサーバーを追加します。GearmanWorkerオブジェクトと同様に、引数なしでaddServer関数を呼ぶと127.0.0.1がジョブサーバーとして追加されます。GearmanClientオブジェクトのdoBackground関数は、ジョブキューにジョブを追加し、すぐに終了します。この関数は第一引数にワーカーの関数名を、第二引数にワークロードを取ります。ワークロードとは、ワーカー側のworkload関数で取り出すことのできる値です。do関数はdoBackground関数と同様の引数を取りますが、結果を受け取るまで待ちに入る点が異なります。この他にも、GearmanClientオブジェクトには、ジョブを追加し最後にまとめて実行する関数なども存在します。詳細についてはPHPマニュアルのGearmanの項を参照してください。

client.phpを実行した後、/tmp/fooディレクトリを見ると次のようになります。

$ ls -l /tmp/foo/
total 80
drwxr-xr-x 12 y-mahata wheel 408 6 7 17:05 ./
drwxrwxrwt 22 root wheel 748 6 7 17:05 ../
-rw-r--r-- 1 y-mahata wheel 1 6 7 16:56 0
-rw-r--r-- 1 y-mahata wheel 1 6 7 16:57 1
-rw-r--r-- 1 y-mahata wheel 1 6 7 16:58 2
-rw-r--r-- 1 y-mahata wheel 1 6 7 16:59 3
-rw-r--r-- 1 y-mahata wheel 1 6 7 17:00 4
-rw-r--r-- 1 y-mahata wheel 1 6 7 17:01 5
-rw-r--r-- 1 y-mahata wheel 1 6 7 17:02 6
-rw-r--r-- 1 y-mahata wheel 1 6 7 17:03 7
-rw-r--r-- 1 y-mahata wheel 1 6 7 17:04 8
-rw-r--r-- 1 y-mahata wheel 1 6 7 17:05 9

client.phpはすぐに終了したはずですが、ファイル生成はきっかり60秒間隔で行なわれたことが確認できます。

注意するべきポイント

doBackground関数やdo関数の第二引数であるワークロードは文字列を受け取ります。したがって、文字列以外のデータ(例えば配列など)をワーカーに渡すときにはシリアライズをする必要があります。また、doBackground関数やdo関数は第三引数としてID(となる文字列)を渡すことができます。この値を指定すると、同一のIDで登録したジョブは一度しか実行されないので、注意してください。

一歩進んだトピック

Gearmanはデフォルトでメモリ上でキューを管理します。もしキューの管理をMySQLやDrizzleで行いたければlibdrizzleを使うことができます。また、ジョブキュー管理ソフトウェアはGearmanの他にも存在します。TheSchwartzQ4Mなどと比較することで、Gearmanの長所と短所をより理解することができるかもしれません。

参考資料

カンファレンスに行こう(その1)

こんにちは、ECナビの開発・運営に携わっております小芝と言います。
昨今、特にIT業界では勉強会ブームです。私も勉強会に出たり、お話したり、開催したりと活動しています。今回は勉強会のでかい版ともいえるカンファレンスの参加について、これまでに得たノウハウをまとめました。

カンファレンスに参加していますか?

ECナビのメンバー(社内ではクルーと言います)は結構頻繁にカンファレンスに参加しています。参加だけではなく、出展、スポンサード、講演、主催など様々な関わり方をしております。
私も、東京Ruby会議01東京Ruby会議03DevLOVE2009FUSION日本Ruby会議2009日本Ruby会議2010などのカンファレンスに参加したり、講演やLTをしたり、実行委員をしたりと、結構カンファレンス充させてもらってます。

しかし社外の知人らを見るに見るにまだまだ参加する習慣が無い方が多いのではないでしょうか。

これは大変勿体無い!

日本でのカンファレンス参加の金銭的負担は比較的軽いというメリットがあります。(開催側は大変ですが)
プログラミング言語Rubyやアジャイルな開発手法に関する活動で有名なプログラマーの方が講演の中で「日本では水と安全とカンファ レンスはタダだと思われている」(DevLOVE2009FUSION講演ビデオの2/6の4:35あたり)という発言が、端的に表された現状かと思います。

カンファレンスに参加することによって、知識や事例、ノウハウなどを集中して知ることができることはもちろん、普段の仕事ではなかなか得られない新鮮な刺激を得ることができ、中長期的なメリットを享受することができます。
より多くの人がより多くの人がカンファレンスに行けるようになればと思うので、カンファレンス参加のコツをまとめました。

じゃあ、どうすればカンファレンスに参加できるのか

以下の4つを行うことで、カンファレンスに参加することができます。
  1. 調べる
  2. 申し込む
  3. 選ぶ
  4. 交渉する

1.調べる

そもそもどのようなカンファレンスがあるのかを調べましょう。カンファレンスによく行く人に尋ねてみる、Googleで検索するなどで簡単に調べられます。
IT勉強会カレンダーにも載っているものが色々あるので、参照してみてください。

2.申し込む

たいていはネット申し込みが可能です。
これは! と思うカンファレンスを見つけたらポチっと参加申し込みをしましょう。
申し込みが始まっていないなら、申し込み開始日を忘れずにスケジュール登録しておきましょう。人気カンファレンスは数日で埋まってし まうことも多々あります。

3.選ぶ

カンファレンスで行われるセッションの中でどれを行こうか予め考えておきましょう。
ここでよくあるアンチパターンは、「受講セッションの詰め混み過ぎ」です。朝から晩まですべてのセッションの枠を埋めてしまうと、ただただしんどいばかりで効果的な参加とはなりません。
自分の中でテーマを絞って、是が非でも出たいセッションとそうではないセッションを区別しておきましょう。

更には、是非ともセッションに出ずに廊下やロビーで過ごす時間を意図的に設定しておきましょう。そういう場所で偶然出会う 突発的な催し物や他の参加者との交流も、カンファレンスの大きな楽しみです。

4.交渉する

会社勤めの方は、努めて業務の一環としてカンファレンスに参加できるように上司と交渉しましょう。
こうすると、会社へのカンファレンス参加レポートが必要となるので、自分は何を得て何を持ち帰りたいかを明確にすることができます。
また、業務でのカンファレンス参加の習慣が定着し、他の勉強会や研修にも参加しやすくなる環境を作ることができます。

なお、ECナビではキャリアアップ支援制度という、カンファレンスや研修など自分のスキルアップのための参加費や研修費を全額支給する枠組みがあり、積極的に活用することが推奨されています。
またそれとは別に、海外カンファレンス参加制度があり、国内カンファレンスのみならず海外カンファレンスへ参加についても支援を行う枠組みがあります。

もちろん、カンファレンスに参加する分の業務の調整も忘れずに行いましょう。とはいえ、リリース直前など何かの締め切り間際でなければ、カンファレンスで1日や2日抜けたところで体勢に影響はありません。
先の業務の予定が決まっていないのであればあればなお好都合です。自分でスケジュールを立てて決定事項としてカンファレンスの予定を入れておきましょう。

次回予告

以上4つを行うことでもうカンファレンスに参加する準備は整いました!
次回はカンファレンスに出てからどういう風に立ち回ればいいかをまとめます。 

お知らせ

ECナビグループのPeXが、世界最大級のプログラム言語Rubyのカンファレンスである日本Ruby会議2010ゴールドスポンサーとして支援しております。
2010-06-15 19:00(JST)からチケットの一般販売が開始されますので、Rubyを使っている方はもとより、ちょっとでもRubyにご興味のある方は是非ご参加下さい。

理系インターン募集!!

こんちには、 システム本部の吉村と申します。
ECナビでは理系インターンを毎年夏に開催しており、 今回で5回目となります。
開催期間は、2010/8/23(月)から2010/9/17(金)までです。

詳細は、ECナビ/理系インターンシップ-Treasure をご覧下さい。

ECナビ エンジニアブログの執筆者たちも、 もちろんインターンのサポートを行ないます。
参加者はカリキュラムに沿った講義・実習を行なうだけではなく、
現場の技術者に話を聞いたり質問できます(カリキュラムと関係ないことでもOKです)。
成長したいあなたのエントリーをお待ちしております。
記事検索
QRコード
QRコード