Amazon EC2 と Groovy と QuartzScheduler と commons-daemon

2012/11/11 のポッキーの日に「AWSで動かすWordPressとその高速化」セミナーに伺いまして、Amazon Web Service を堪能して参りました。 🙂

AWSで動かすWordPressとその高速化

アマゾン ウェブ サービス(AWS) にはウェブデザイナーにとっても魅力的なサービスがたくさんあります!
今回はAmazon Web Servicesのテクニカルエバンジェリストである堀内さんをお招きし、すぐにでも使えるサービスを実際に体験しながら(ハンズオンで学びならが)その魅力たっぷりのサービスについてを学びます。

また、AWS上でWordPressを使えるようにし、さらに高速化する方法について、 株式会社デジタルキューブ の小賀さんをお招きし、実際に体験しながらその方法についてを学びます!

tenpura さんや小賀さんにも久しぶりにお会いできまして楽しいひとときでした。

仕事柄、仮想化基盤には慣れてはいるものの、超高速チューンされた WordPress である「AMI 網元」がほとんどワンクリックでデプロイされる様子や、ストレージのスナップショットバックアップはやはり魅力的なフューチャーでした。

WordPress AMI – 超高速 WordPress AMI 網元

EC2のMicro Instance(無償枠あり)を利用すれば、圧倒的にハイパフォーマンスなWordPress環境をコンパネ操作のみで誰でも簡単に構築できます。WordPressの高速化対策、スマートフォン対策に効果的です。

現在のクラウドと呼ばれるサービスの根幹をなしているのは間違いなく仮想化技術です。 そして WordPress を実用的にクラウドにのせる試みはすごく面白いものでした。:)

AMI 綱元も使える Amazon EC2 のマイクロインスタンス(アカウント登録から1年無料とのこと)。

EC2 は root がもらえる IaaS。

むふふ..(←脱線したようだ

というわけで、Groovy と QuartzSchedulercommons-daemon を使って怪しい(?)デーモンを動かしてみることにしました。 😛

デーモンは処理(スレッド)の途中終了をつくるのが結構大変だったりしますが、QuartzScheduler にそのへんを任せると定期実行する常駐ものが簡単につくれます。

以下の例は 20秒おきにログをはくデーモンです。実際には CronScheduleBuilder.cronSchedule で時間を決め、QuartzJob::execute() に好きな処理を実装します。

QuartzDaemon.groovy

package net.maple4ever.daemon

import org.apache.commons.daemon.*
import org.quartz.*
import org.quartz.impl.*
import org.apache.log4j.Logger

class QuartzDaemon implements Daemon {

    def static logger = Logger.getLogger(QuartzDaemon.class)
    def static daemon = new QuartzDaemon()
    def static sched = (new StdSchedulerFactory()).getScheduler();

    @Override
    public void init(DaemonContext arg0)
        throws DaemonInitException, Exception {
        // every 20 seconds
        def job = JobBuilder.newJob(QuartzJob.class)
            .withIdentity("job1", "group1")
            .build();
        def trigger = TriggerBuilder.newTrigger()
            .withIdentity("trigger1", "group1")
            .withSchedule(
                CronScheduleBuilder.cronSchedule("0/20 * * * * ?"))
            .build();
        sched.scheduleJob(job, trigger);
    }

    @Override
    public void start() throws Exception {
        logger.info("START QuartzDaemon")
        sched.start()
    }

    @Override
    public void stop() throws Exception {
        logger.info("STOP QuartzDaemon")
        sched.shutdown(true)
    }

    @Override
    public void destroy() {
        logger.info("DESTROY QuartzDaemon")
    }

    static main(args) {
        daemon.init(null)
        daemon.start()
        System.in.withReader {
            print  'Hit any key to exit.'
            println it.readLine()
        }
        daemon.stop()
    }
}

class QuartzJob implements Job {

    def static logger = Logger.getLogger(QuartzJob.class)

    def QuartzJob() {
    }

    @Override
    public void execute(JobExecutionContext context)
        throws JobExecutionException {
        // execute job
        JobKey jobKey = context.getJobDetail().getKey()
        logger.info("QuartzJob says: " + jobKey + " executing at " + new Date())
    }
}

commons-daemon のインターフェースに従ってクラスを定義して、init で QuartzScheduler のジョブ定義。 commons-daemon からのの start、stop をそのままQuartzSchedulerにパスしてあげればOKです。

また、この例のように static main メソッドをつくっておくとデバッグ時にデーモンではなくそのまま実行できるので便利です。

できたら groovyc でコンパイルしたクラスファイルを、commons-daemon の jsvc を次のようなシェルから起動してあげます。

setenv.sh

#!/bin/sh

SCRIPT_HOME=/opt/daemon
JSVC_USER=hiromasa

JAVA_HOME=/usr/lib/jvm/java

GROOVY_LIB=/opt/groovy-2.0.5/embeddable/*
LIB_HOME=$SCRIPT_HOME/lib
LIB_PATH=$GROOVY_LIB:$LIB_HOME/commons-daemon/*:$LIB_HOME/log4j/*:$LIB_HOME/quartz/*
LIB_PATH=$LIB_PATH:$SCRIPT_HOME/script
LIB_PATH=$LIB_PATH:$LIB_HOME/twitter4j/*:$LIB_HOME/javamail/*

#for command line(ex. $GG Foo.groovy)
export GG="java -cp $LIB_PATH groovy.ui.GroovyMain"

# twitter4j とか javamail とかは必要ありません。(ぼくの別デーモンで使われています 🙂

export している $GG はスクリプト作成時の実行用で、シェルから . ./setenv.sh などとして環境をもってきた後に、$GG QuartzDaemon.groovy とするとコンパイルせずに static main から実行できるようなっています。(UNIX 上の vi などで直接 .groovy をかくときに便利です)

ディレクトリ構成と lib は以下のような感じです。

[hiromasa@]$ ls -laF
合計 28
drwxr-xr-x 7 hiromasa hiromasa 4096 11月 18 08:57 2012 ./
drwxr-xr-x 5 root     root     4096 11月 18 09:38 2012 ../
drwxrwxr-x 2 hiromasa hiromasa 4096 11月 18 09:45 2012 bin/
drwxrwxr-x 7 hiromasa hiromasa 4096 11月 18 01:53 2012 lib/
drwxrwxr-x 2 hiromasa hiromasa 4096 11月 18 09:48 2012 log/
drwxrwxr-x 4 hiromasa hiromasa 4096 11月 18 10:56 2012 script/
drwxrwxr-x 2 hiromasa hiromasa 4096 11月 18 09:48 2012 tmp/
[hiromasa@]$ ls -laF lib/*
lib/commons-daemon:
合計 32
drwxrwxr-x 2 hiromasa hiromasa  4096 11月 17 11:43 2012 ./
drwxrwxr-x 7 hiromasa hiromasa  4096 11月 18 01:53 2012 ../
-rw-rw-r-- 1 hiromasa hiromasa 24242  2月 23 23:22 2012 commons-daemon-1.0.10.jar

lib/log4j:
合計 488
drwxrwxr-x 2 hiromasa hiromasa   4096 11月 17 12:01 2012 ./
drwxrwxr-x 7 hiromasa hiromasa   4096 11月 18 01:53 2012 ../
-rw-rw-r-- 1 hiromasa hiromasa 489883  5月  6 11:01 2012 log4j-1.2.17.jar

lib/quartz:
合計 1212
drwxrwxr-x 2 hiromasa hiromasa   4096 11月 18 07:15 2012 ./
drwxrwxr-x 7 hiromasa hiromasa   4096 11月 18 01:53 2012 ../
-rw-r--r-- 1 hiromasa hiromasa 608376  5月 20 11:12 2011 c3p0-0.9.1.1.jar
-rw-rw-r-- 1 hiromasa hiromasa 578548  8月  6 13:28 2012 quartz-all-2.1.6.jar
-rw-r--r-- 1 hiromasa hiromasa  25496  5月 19 20:22 2011 slf4j-api-1.6.1.jar
-rw-r--r-- 1 hiromasa hiromasa   9753  5月 19 21:02 2011 slf4j-log4j12-1.6.1.jar

quartz.sh

#/bin/sh

# setup
. /opt/daemon/bin/setenv.sh

RUN=net.maple4ever.daemon.QuartzDaemon

# check args
if [ $# -ne 1 ]
then
	echo "usage: quartz.sh [start|stop]"
	exit 1
fi

ret=9
case $1 in
	start )
		echo "starting daemon..."
		$SCRIPT_HOME/bin/jsvc \
			-pidfile $SCRIPT_HOME/tmp/$RUN.pid \
			-user $JSVC_USER \
			-home $JAVA_HOME \
			-cp $LIB_PATH \
			$RUN
		ret=$?
		;;
	stop )
		echo "stoping daemon..."
		$SCRIPT_HOME/bin/jsvc \
			-stop \
			-pidfile $SCRIPT_HOME/tmp/$RUN.pid \
			$RUN
		ret=$?
		;;
esac

exit $ret

手抜きシェルですが、./quartz.sh start|stop でデーモンの常駐制御ができます。

hiromasa 24923     1  0 09:48 ?        00:00:00 jsvc.exec -pidfile /opt/daemon/tmp/net.maple4ever.daemon.QuartzDaemon.pid -user hiromasa -home /usr/lib/jvm/java -cp /opt/groovy-2.0.5/embeddable/*:/opt/daemon/lib/commons-daemon/*:/opt/daemon/lib/log4j/*:/opt/daemon/lib/quartz/*:/opt/daemon/script:/opt/daemon/lib/twitter4j/*:/opt/daemon/lib/javamail/* net.maple4ever.daemon.QuartzDaemon
hiromasa 24924 24923  0 09:48 ?        00:00:06 jsvc.exec -pidfile /opt/daemon/tmp/net.maple4ever.daemon.QuartzDaemon.pid -user hiromasa -home /usr/lib/jvm/java -cp /opt/groovy-2.0.5/embeddable/*:/opt/daemon/lib/commons-daemon/*:/opt/daemon/lib/log4j/*:/opt/daemon/lib/quartz/*:/opt/daemon/script:/opt/daemon/lib/twitter4j/*:/opt/daemon/lib/javamail/* net.maple4ever.daemon.QuartzDaemon

このように jsvc プロセスが 2つできれば成功です。 🙂

commons-daemon と QuartzScheduler を使えば、cron などを使うのと違って簡単に無理なく状態遷移を制御できますので良いのではないかと思います。

さぁ、Amazon EC2 のマイクロインスタンスでどんなデーモン動かしましょう。 いろいろアイディアがでてきますが、ネットワークの中で生き続けるプログラムってやっぱり面白いですね。 😀

このエントリーをはてなブックマークに追加

コメントを残す

メールアドレスが公開されることはありません。