Home

うしねずみの技術メモ

eclipseでphpコードをデバッグするとApacheが落ちる

どうも、うしねずみです。
最近eclipseを使ったPHPの開発でハマった(ハマっている)ので書きます。

いまPHPの開発にeclipseのパッケージの一つである、Eclipse for PHP Developersを使っているのですが、どうもコレ、Vistaとの相性が悪いらしい。というより、PHPのデバッガとVistaの相性が悪いらしい。

この開発環境は
Window>Preference>PHP>Debug
のPHP Debuggerの項目でXDebugとZend Debuggerを選べるのですが、ネットで調べてみるとVistaPCでXDebugを使ってデバッグするとApacheが落ちるらしい。私はZendを使っていたので関係ないかと思いきや、Apacheが落ちる落ちる。

最初は2,3回に1回だったので落ちたら再起動して我慢していたのですが、そのうち落ちる頻度が増え、PHPのソース内で読み込んでいるswfファイル(Flexアプリケーション)の挙動まで安定しなくなってきました。もう少し具体的に言うと、Flexアプリ側からPHPにアクセスしてDBからデータを拾ってほしいのですが、PHPから正常に値が返ってこない。最初はFlexアプリ側でどんなに探してもバグらしきものが見つからなくて困りました。しかしブラウザからアクセスすると正常な挙動を示すので、どうやらPHPのデバッガのせい。しまいには、eclipseまで落ちまくるようになって、ホントに困りました。

今のところブラウザからアクセスした感じではアプリ全体にバグがなさそうなのでいいですけど、このままだとPHP部分とFlex部分の結合テストができないなー。。。
あー、面倒だ。

SVNでマージしようとしたらRetrieval of mergeinfo unsupported

どうも、うしねずみです。

最近Flexアプリをeclipse + FlexBuilderプラグイン + subclipseで開発しています。subclipseって言うのはeclipse上で使えるSVNクライアントのプラグインです(他にはsubversiveというのが有名らしい)。

それで、今日コードをSVNリポジトリのtrunkにマージしようとしたら問題が。。。

Retrieval of mergeinfo unsupported by “svn+ssh://hogehoge”

って言われるのです。
どうやら、SVNサーバのバージョンが古くて、SVNクライアント側が必要としている情報が見つからないらしい。ちなみに subclipseのバージョンは1.6.5で、SVNサーバのバージョンは1.2.3。SVNのバージョンは”svn –version”で確認できます。

さて問題が起きた流れを説明してみます。

まず、マージしたいプロジェクトを右クリックして「Team」>「マージ」を選択します。するとマージのダイアログが立ち上がります。
ここです!
私は機能ブランチが完成してトランクにマージしようとしていたので「Reintegrate a branch」 を選びました。これが失敗の原因。 私が使ってるバージョンのsubclipseでは、ブランチを再統合しようとすると、マージすべきリビジョンの範囲をブランチの過去のマージの情報から勝手に取得してくれるっぽいのですが、SVNサーバのバージョンが古いと「ブランチの過去のマージの情報」っていうのが残らないらしく、「そんな情報はないよ」みたいに怒れられるっぽいです。

なので「Merge a range of revisions」を選んで、手動でマージするリビジョン範囲を指定しましょう。
なお、この場合の注意点は、今回どのリビジョンまでをマージしたかを記録しておかないと、次のマージの時にマージすべきリビジョン範囲が分からなくなります。
例えば今回リビジョン1000までマージしたのなら、次回のマージは1001~HEADリビジョンとなるはずです。

SVNはマージの方法とか理解しにくくていつも苦労します。それから「リビジョン」に対する考え方がSVNのコマンドラインクライアントとtortoiseSVN(メジャーなSVNクライアントソフト)では違うらしく、注意が必要です。

TortoiseSVN とコマンドラインクライアントを比較すると、リビジョン範囲を指定する方法に重要な違いがあります。これを思い浮かべる簡単な方法は、フェンスの柱と、フェンスの板について考えることです。コマンドラインクライアントでは、 で指定した 2 本の 「フェンスの柱」 のリビジョンを使用して、マージする変更を指定します。TortoiseSVN では、「フェンスの板」 を使用して、マージする変更セットを指定します。マージするためのリビジョンを指定するログダイアログを使用する場合、チェンジセットとしてどこに各リビジョンが現れるか、明白になるためです。かたまりとしてリビジョンをマージしていると、subversion book にある方法では、今回 100-200 をマージし、次回に 200-300 をマージすることになります。TortoiseSVN では、今回 100-200 をマージし、次回に 201-300 をマージします。この違いは、メーリングリストでたくさんの論争を巻き起こしてきました。私たちは、コマンドラインクライアントと違うことを認めます。しかし、大多数の GUI ユーザにとって、私たちが実装した方法の方が理解しやすいと信じています。

でも、SVNが無い開発はデグレしまくるだろうし、恐ろしくてやってられません。以前マージを手動で(WinMergeというマージツールを使って)一つずつファイルを開いてやったことがありましたが、死にました。
これからもお世話になります、SVN。

Flexで画像のロードが終わるまで待機したい(Sleepが無い!!)

どうも、うしねずみです。
Flexでの画像のロードについて、悩んだので書いてみます。どうやらFlexにはSleep関数が無いようで、そのせいで色々悩みました。まずは、画像をロードする方法をまとめてみます。

方法その1: LoaderをそのままaddChildする

例えばロードした表示するだけの時は以下のように書きます。

public function test():void
{
    var loader:Loader = new Loader();
    var urlRequest:URLRequest = new URLRequest("http://example.com/hoge.jpg");
    var uic:UIComponent = new UIComponent;
    loader.load(urlRequest);
    uic.addChild(loader);
    this.addChild(uic);
}

loaderをuicにaddChildしたものをaddChildすれば画像が表示されるというちょっと不可思議な感じ。

方法その2: COMPLETEイベントのリスナーを登録しておく

方法その1だと、画像を表示するだけなら出来ましたが、画像を扱ってどうにかしたいときは困ります。そういう時は以下の方法。

public function test():void
{
    var loader:Loader = new Loader();
    var urlRequest:URLRequest = new URLRequest("http://example.com/hoge.jpg");
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
    loader.load(urlRequest);
}
private function loadComplete(event:Event):void
{
    var loaderInfo:LoaderInfo = event.target as LoaderInfo;
    var bitmap:Bitmap = loaderInfo.content as Bitmap;
    // 画像を使った何らかの処理をここに書く
}

4行目でloadComplete関数をロード完了時のイベントリスナに登録しています。よって、画像の読み込みが終わるとloadComplete関数に制御が飛んで、その中で画像を使った処理ができます。test関数はロードが完了する前にリターンされます。よってtest関数を呼んだ側の処理が画像のロードの間待たされるわけではありません。

さて、ここで問題が

実はこの「ロードが終わる前に制御が戻る」という性質が厄介だったりするのです。最近画像のラッパーを作っていたのですが、その時に「画像のロードが終了するまで呼び出し側の実行を待機したい」ということがありました。例えば以下のようなラッパーを作ったとします。

public class MyImageWrapper
{
    public var imageAddress:String;
    public var imageData:Bitmap;
    public function MyImageWrapper(address:String)
    {
        // コンストラクタ内で画像のロード処理
        var loader:Loader = new Loader();
        var urlRequest:URLRequest = new URLRequest("http://example.com/hoge.jpg");
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
        loader.load(urlRequest);
    }
    private function loadComplete(event:Event):void
    {
        // ロードが終わったらメンバ変数に格納しておく
        var loaderInfo:LoaderInfo = event.target as LoaderInfo;
        this.imageData = loaderInfo.content as Bitmap;
    }
}

このクラスは、画像のアドレスを渡してnewすると、コンストラクタ内で画像をロードしておいてくれるクラスです。これだけだとアドレスと画像を持っているだけですが、他にもこの画像に関する様々な情報をまとめて持たせるためのクラスです。
さて、このクラスは例えば以下のように使われます。

var myImage:MyImageWrapper = new MyImageWrapper("http://example.com/hoge.jpg");
this.addChild(myImage.imageData);

ここで問題が生じるのです。MyImageWrapperのコンストラクタはロード処理を開始するだけで、ロードが終わるまで待ちません。なのでまだMyImageWrapperのloadCompleteが呼ばれないうちに制御が返ってきます。すると2行目でまだmyImage.imageDataはnullなので、「nullはaddChildできません」というエラーになるのです。
じゃー答えは簡単。imageDataがnullじゃなくなるまで待てばいいんだな、と思って次のように書き変えました。

var myImage:MyImageWrapper = new MyImageWrapper("http://example.com/hoge.jpg");
while(myImage.imageData == null){
    ;  // 画像のロードが終了するまでは空ループ
}
this.addChild(myImage.imageData);

ところが、このソースを実行してみると、永久にloadCompleteが呼ばれず、5行目にたどりつくことができません。Flex(Flash)の中で「CPUが空き状態になるまでloadCompleteの実行は待機する」という仕様になっているのでしょうか。

じゃあ空ループじゃなくてSleepすればいいんだな。と思って調べたのですが、どうやらFlexにはSleepが無い模様。
さて、困った。

Timerを使って実装することもできなくはないです。以下のようになります。

var myImage = new MyImageWrapper("http://example.com/hoge.jpg");
var timer:Timer = new Timer(100, 0);
timer.addEventListener(TimerEvent.TIMER, function (te : TimerEvent):void{
    if(myImage.imageData != null){
        var uic : UIComponent = new UIComponent();
        uic.addChild(myImage.imageData);
        this.addChild(uic);
        Timer(te.target).stop();
    }
});
timer.start();

タイマは、一定時間ごとに登録したリスナを呼んでくれます。ここでは無名関数をその場で定義して登録しています。つまりこのタイマは100msごとに画像のロードが終わってるかどうかをチェックして、もし終わってたらaddChildを済ませて、自分自身(タイマ)を止めてくれます。
しかしこれはこれでなんか面倒だしすっきりしません。また、timer.start()のところで実行が待機するわけではないので別のところで画像データを使いたくなったときに既に画像のロードが終わっている保証がありません。

例えばユーザには「画像ロードボタン」と「画像編集ボタン」が提示されているとします。「画像ロードボタン」はユーザが指定したURLから画像をロードしてくるボタンです。「画像編集ボタン」はロードした画像に何か編集を加えるボタンです(モノクロに変換するなど、何でもよい)。仮にタイマを使っても、「画像ロードボタン」を押したあとロードが完了する前に制御がユーザに戻ってしまいます。その後すぐに「画像編集ボタン」を押されると、「画像編集ボタン」のイベントハンドラは既に画像が読み込まれていることを期待して読みに来ますが、実際は画像データはありません。

結局、対策としては、画像がロード済みかどうか(null)じゃないかどうか、使う前に毎回チェックして、ロードが済んでいなかったら何もしないっていうこと。それしかないのかなー。
なんとかSleep的な処理ができないかなーと思って疑似Threadライブラリ(そうめん?)なんかも試してみた。Threadが終了するまで待つことができるjoin関数が使えるかと思ったけど、Threadの終了を待てるのはThreadだけのようで、Threadではない実行部分で何かの処理を待機することはできない模様。
完全に手詰まりです。

ところで、Sleepが無い理由を自分なりに考えてみた。
Flexは基本的にイベント駆動型のアプリを作るためにあるはずなので、一つのイベントが全体をSleepさせるような処理を書くべきではない、ということなのだと思う。
でも、なんか、かゆい所に手が届かないむずむずというか、、、初めてSchemeを触った時に「破壊的代入を行わないプログラミング」が難しいパズルのようでどうしても頭に入ってこなかった時のような気持ち悪さ。
まー、修行が足りないってことですかね。

OpenCVのcvLoadImageの戻り値がNULLになる

どうも、うしねずみです。
今日は小ネタです。

最近、ちゃんとファイルがあるのにOpenCVのcvLoadImageがNULLしか返さないという現象に遭遇しました。どうやら画像のロードが失敗している様子。パスが間違っているかなーと思ってロードするファイルパスを確かめるけど、何度見ても合ってる。しまいにはエクスプローラのアドレス欄から絶対ファイルパスをコピペしてみたけど、それでもNULLしか返ってこない。

原因はVisual StudioとVistaでした。
以前どこかで「Visual StudioをVistaで使う時、管理者権限で実行しないと不具合が起きることがある」という記事を見たことがあったのですが、まさにそれ。Visual Studioをいったん閉じて、管理者権限で実行すると、なんの問題もなくプログラムが実行できました。

ほんと、こういう分かりにくいの、やめてほしいです。。。
※実行ファイルを右クリックして、「プロパティ」を開き、「互換性」タブの「管理者としてこのプログラムを実行する」にチェックを入れておくと、ダブルクリックで起動するときも含めて常に管理者権限で実行されます。

ecpliseのout of memory error

どうも、うしねずみです。
最近eclipseのout of memory errorにはまったので書きます。

ことの発端は、PHPとFlex Builderを両方使いだしたこと。
eclipseの最新版(2009年11月6日現在)のgalileo(3.5系)にはEclipse for PHP Developersがあります。でもFlex Builderのプラグインはeclipseの3.2、3.3、3.4にしか対応してないので、この中で最新のganymede(3.4系)にインストールしたのです。

このときeclipseがPCに二つあります。
インストールフォルダはeclipse_galileo、eclipse_ganymedeという風に名前を変えたので問題ないのですが、実行ファイルはどちらもeclipse.exeです。これで何が困るかって、ランチャーソフトで起動するときです。私はLaunchyというランチャーソフトを使っています。キーボードで実行ファイル名を入力するとインクリメンタルサーチしてくれる便利なランチャーです。同じ名前の候補があったら下に候補がプルダウンしてくれるので、同じ名前のソフトが複数あっても問題無いといえば無いのですが、いちいちプルダウンから選択するのはランチャーソフトの手軽さが半減します。

じゃーどうするかって。
実行ファイルの名前を変えればいいじゃなーい。さすがにトラブルが起きそうな気がしたのですが、eclipse.exeをganymede_eclipse.exeに変更しても無事、起動。ここで無事起動したからあとあと、はまることになりました。

この後、Google App Engine for Javaのプラグインも入れて、作業を開始。
ところが、Flexのコードをコンパイルしようとするとout of memory error。

そりゃーね。GAEのプラグインを疑いますよ。だってこれ入れた後にトラブル発生なんだもの。いろいろ調べてみても、JavaのVMに割り当てるメモリ容量を増やせとかしか書いてない。まさかね。実行ファイルをリネームした副作用がout of memory errorなんてね。気づきませんよ。

ま、結局両方ともeclipse.exeに名前を戻して、無事解決しましたとさ。

Vista PCでFlex Builder 3のプラグイン版をインストールした

どうも、うしねずみです。

Flex Builder 3のPlug-in版をeclipseのganymede(3.4系)にインストールしましたが、少しトラぶったので書きます。

まず、ganymedeのclassic eclipseをプロジェクトページからダウンロード。そしてアドビのダウンロードページからAdobe Flex Builder 3 Professional Eclipse plug-in (60 day trial)のWindows, Japaneseをダウンロード。

他の解説しているサイトなどを見ると「ダウンロードしてきたFB3_WWEJ_Plugin.exeを実行せよ」と書いてあるのだが、ダウンロードできるものはexeファイルではなくFB3_WWEJ_Pluginという名前。「まさか、ね」と思いつつファイル名に.exeを追加してみると、何故か実行できる。
えと、まーよくわからんけど、いいや。

ウィザードに従って進むと、どのeclipseにプラグインをインストールするか?といった趣旨のことを聞かれる。さっきダウンロードしてきたeclipseのフォルダを指定。インストールはこれで無事終了。

ところが、eclipseを起動して、新しくFlexプロジェクトを新規作成しようとすると、以下のようなエラーを吐かれる。

The selected wizard could not be started.
Reason: Plug-in com.adobe.flexbuilder.editors.common was unable to load class com.adobe.flexbuilder.editors.commons.ui.project.wizards.NewFlexProjectWizard

 初期設定ファイルを作るためなのかどうかわからないが、インストール後に初めてeclipseを起動するときだけ「管理者として実行」しないとだめらしい。というわけで、eclipse.exeを右クリックして「管理者として実行」を選択。これで問題なく新規Flexプロジェクトを作れました。
2度目以降は管理者として実行しなくても問題なく使えるようです。

PHPのアカウント管理ライブラリ、phpUserClassを使ってみた

どうも、うしねずみです。

PHPのアカウント管理やログイン、ログオフ管理に便利なライブラリのphpUserClassというものを使ってみました。
こちらでダウンロードできます。
http://phpuserclass.com/

PHPとMySQLを使って、アカウントの作成、アクティベーション、ログイン処理、ログアウト処理などアカウント管理に関することが簡単にできるようになっています。access.class.phpというファイルの中に一つだけクラスが定義されていて、このクラスのメンバ関数を使ってログイン、ログアウトなど、いろんな処理をします。関数名も分かりやすくつけられているので、使い方が分からなくなることもあまりないかも。
使うときは以下のような流れで使うことになります。

MySQLに必要なテーブルを作る

これは簡単です。ダウンロードして解凍したphpUserClassのフォルダの中にある「example2.php」というファイルの先頭に、そのテーブルを作るために必要なSQL文がコメントで書いてあります。こんな感じで。

 CREATE TABLE `users` (
 `userID` mediumint(8) unsigned NOT NULL auto_increment,
 `username` varchar(50) NOT NULL default '',
 `password` varchar(100) NOT NULL default '',
 `email` varchar(150) NOT NULL default '',
 `active` tinyint(1) NOT NULL default '0',
 PRIMARY KEY  (`userID`),
 UNIQUE KEY `username` (`username`),
 UNIQUE KEY `email` (`email`),
 KEY `active` (`active`)
 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

このSQL文を実行するだけで必要なテーブルが作成されます。ここでは、「account_data」というDBでこのSQL文を実行して「users」というテーブルを作成したとして、以下の説明を進めていきます。

アカウント作成

アカウントの作成は以下のようにします。

require_once 'access.class.php';
$dbConnection = mysql_connect('localhost', 'root');
$settings = array('dbName' => 'account_data');
mysql_select_db('account_data', $dbConnection);
$user = new flexibleAccess($dbConnection, $settings);

$data = array(
'username' => $_POST['username'],
'email' => $_POST['email'],
'password' => $_POST['pwd'],
'active' => 1
);
$userID = $user->insertUser($data);
//The method returns the userID of the new user or 0 if the user is not added
if ($userID==0)
    echo 'User not registered';//user is allready registered or something like that
else
    echo 'User registered with user id '.$userID;

このコードの五行目までは全てのページに書かなければいけないおまじないです。一行目はphpUserClassのファイルの読み込み。二行目はMySQLに接続する処理。第一引数はMySQLのユーザ名で、第二引数はMySQLのパスワードです。第一引数はサーバ名で、第二引数はMySQLのアカウント名です。三行目はアカウント情報が格納されているDBを指定するオプションを付けています。先程説明したとおり「account_data」というDBであるとします。四行目ではMySQLを実際にそのDBに切り替えておきます。五行目はphpUserClassが提供するクラスであるflexibleAccessというクラスをnewします。

ここで作られた$userというオブジェクトのメンバ関数を呼ぶことでいろんな処理を実現します。例えばアカウントを作るときは13行目のように$userのinsertUserメソッドを呼びます。引数には必要な情報を格納した連想配列。activeというのはアカウントを作成と同時にアクティブにするか、それとも仮登録メールを送信して、それに書かれたリンクをユーザがクリックした後にアクティブにするかを指定します。1を入れておくと、メール送信は行わず、作成と同時にアクティベートされます。連想配列に入れたデータのサニタイズはphpUserClassが内部的にやってくれるので全く気にする必要はありません。楽ちんですね。さらにパスワードもsha1かmd5を使って(デフォルトではsha1)暗号化(?)して保存されます。

ログイン

次はすでにアカウントを作った人がログインする処理です。
これも比較的簡単。まず先頭五行におまじないを書きます。次に$userのloginメソッドを呼びます。これでだけです。第一引数はユーザ名。第二引数はパスワード。第三引数はログイン画面でよく見る「次回から自動的にログインする」っていうチェックマークに相当すると思います(たぶん)。

require_once 'access.class.php';
$dbConnection = mysql_connect('localhost', 'root');
$settings = array('dbName' => 'account_data');
mysql_select_db('account_data', $dbConnection);
$user = new flexibleAccess($dbConnection, $settings);

if ( !$user->login($_POST['uname'],$_POST['pwd'],$_POST['remember'] )){
//Mention that we dont have to use addslashes as the class do the job
    echo 'Wrong username and/or password';
} else {
//user is now loaded
//ログイン後の処理
    header('Location: main.php'); // たとえばメインページにリダイレクト
}

ログインチェック

で、実際そのページに飛んできた人がログインしているかどうかはどうやって確かめるかと言うと、これも簡単。以下のようにします。

require_once 'access.class.php';
$dbConnection = mysql_connect('localhost', 'root');
$settings = array('dbName' => 'account_data');
mysql_select_db('account_data', $dbConnection);
$user = new flexibleAccess($dbConnection, $settings);

if ( $user->is_loaded() ){
    echo 'OK';
} else {
    echo 'NG';
}

先頭五行目までは例のごとくおまじない。そして$userのis_loadedメソッドを呼んで、trueが返ってきたらログイン済みのちゃんとしたユーザです。簡単ですよね?

さて、ここで不思議に思った人もいるかもしれません。なぜなら、ログインしているかどうかをチェックしているはずなのに、ユーザ名とか一切指定していないですね。セッション管理のコードを自前で書いたことがある人なら、session_start関数が呼ばれていないことも気になるかもしれません。でも、そこは一切気にしない。全部phpUserClassにお任せ。

実はphpUserClassの中でsession_start関数が呼ばれていて、セッション変数としてユーザ名を持っているため、現在のユーザが誰かとかは全く気にすることなくログインチェックができるのです。便利っすね、ホント。
ちなみにユーザ名が知りたいときは以下のようにします。

$user->get_property('username');

ログアウト

これも簡単。以下のようにするだけ。logoutメソッドの引数は、ログアウト処理後のリダイレクト先です。

require_once 'access.class.php';
$dbConnection = mysql_connect('localhost', 'root');
$settings = array('dbName' => 'login_data');
mysql_select_db('account_data', $dbConnection);
$user = new flexibleAccess($dbConnection, $settings);

$user->logout('http://www.usinezumi.com/hoge.php');

まとめ

いやー、簡単です!!
やり方は詳しく見てませんがアクティベーション処理もできるようです。アクティベーション処理というのは、アカウント登録のときにメールアドレスを入れると、そのメールアドレス宛てに仮登録メールが送られてきて、そのリンクをクリックした時点で初めてアカウントを有効にするっていう、よくあるアレ。設定をちゃんとやるとメールを自動で送ってアクティベーション処理までやってくれるという。すごいっすね。ログアウト処理時にセッション変数を空にしたりとかセキュリティにも気を使われているようで安心です(私はあんまり詳しくないですが)。
どんどん活用させて頂きます!

Amazon EC2とGoogle App Engineのコスト比較

どうも、うしねずみです。

最近趣味で作ろうとしているWebサービスを、クラウド上に実装しようとしていて、AmazonのEC2を使うかGoogle App Engineを使うかを選ぶ必要がありました。そこで気になるのはやっぱり、実際どっちがどれぐらい得なんだろうねと。というわけで、自分が想定しているサービスで月毎にかかる料金を試算してみました。それが以下のグラフです。

Amazon EC2 v.s. Google App Engine

Amazon EC2 v.s. Google App Engine

横軸は自分の想定しているサービスの登録ユーザ数。縦軸はその時のコスト(米ドル)です。グラフをみると最初はGoogleが低いものの、途中から逆転しています。両方が交差している点は2400万ユーザの1万7000米ドルくらいです。(試算の細かい前提は後で書きます)

しかし、自分で出しといて言うのもなんですが、このグラフは眉唾ものです。何故かというと、この試算にはGAEの利用CPU時間に対する課金が一切含まれていません(実際どれぐらい消費するのか予想しづらい。「そこをベンチマークするのがお前の仕事だろ」って言われそうですが)。実際にはGAEではリクエストを受けた時やBigtable(GAEのデータベース)にアクセスした際に利用されたCPU時間に対して課金されるわけで、CPU時間に対して課金されないAmazon EC2と並べて比べるのは非常に不公平です。

しかしGoogleにばかり有利な試算ではありません。実際ユーザ数が増えてくると、Amazon EC2では1つのインスタンスではリクエストをさばけなくなってくるはずです。そうなったときサイトのパフォーマンスを維持するためにインスタンス数を増やせば、当然インスタンス利用時間にかかる課金がもっと増えてきます(GAEは「マシンと数」みたいな概念は無く、自動でスケールするので気にする必要が無い)。さらにインスタンスを2つ以上にした場合はロードバランサーをオプションとして付けなければならなくなるため、ロードバランサーの処理したトラフィックに対する課金も含まれてきて、EC2のコストはこの試算よりも膨れ上がるはずです。

結局今回の試算で効いているのは、EC2はデータ転送量が増えるほどデータ転送量に対する課金の単価が下がるのに対し、GAEではどれだけデータ転送量が増えても課金の単価が一定であるという点だけです。といっても全く意味がない試算をしたわけでは無くて、2400万ユーザという驚異的なユーザ数を獲得するまではGoogleの方が安いということはわかりました。そして、2400万ユーザも居たら、その時のコストである1万7000米ドル/月なんて余裕で回収できるんじゃないかっていうことも。
ま、CPU時間に対する課金とか、かなり効いてきそうなので、やっぱり意味の無い試算だったような気もします。

ちなみにこの試算ではEC2とGAEが逆転するのは2400万ユーザなんてかなり多い数になってますが、これは私が想定しているサービスの1リクエストあたりのデータ転送量が約4kBと少ないためで、画像なんかをたくさん置いている場合はもっともっと少ないユーザ数で逆転すると思います。

※今回の試算の前提
1ユーザ、1日あたりのリクエスト数 : 50
1リクエストあたりのデータ転送量(out) : 3.95kB
1リクエストあたりのデータ転送量(in) : サービスの特性上無視できる量なので無視
1アカウントあたりの情報量(ストレージの消費) : 0.2kB (←いや、今考えると少ないな)
サービスが履歴保存に使う最大容量(ストレージの消費) : 約5GB (←これを超えると古い情報から破棄していく)
その他 : EC2ではsmallインスタンスを24時間稼働しっぱなし。

GAEのBigtableへのデータのインポートとエクスポートをやってみた

どうも、うしねずみです。

Google App Engineで使えるデータベースはBigtableというのですが、このデータベースへのデータのインポートとエクスポートをやってみました。結論から言うと、日本語を含まないデータのインポートだけしかできませんでした。以下にまとめてみます。

基本的にはGoogleのスタートガイドのページのここに解説されています。だたし、気をつけなければいけないのは、データのインポートおよびエクスポートは英語版のSDKでしかできません(2009年8月3日現在)。どうやらSDKの1.2.1から加わった機能のようです。日本語のダウンロードページで公開されているのは1.2.0なので指示通りのコマンドを入れてもエラーを吐かれます。ですので英語のダウンロードページから最新版(2009年8月3日現在は1.2.3)をダウンロードしてインストールしてください。

これであとは解説ページ通りに行えば行けます。まだローカル環境でしか試してないのですが、1087件のレコードをインポートすることに成功しました。ただしこれは日本語を含まない場合のみで、日本語を含むデータでは

[ERROR   ] Error in worker [Thread-6]: ‘ascii’ codec can’t decode byte 0×82 in p
osition 0: ordinal not in range(128)

というエラーが出てアップロードできませんでした。
また、エクスポート機能についてはGAEのサイトで以下のように言及されています。

2009 年 4 月: Python SDK リリース 1.2.1 には、データのアップロード機能と類似した、データのダウンロード機能の初期バージョンが含まれています。この機能はまだ appcfg.py では使用できませんが、試したい場合は、bulkloader.py をご覧ください。マニュアルを含めたこの機能の完全版を、今後の SDK バージョンでリリース予定です。

さっそくSDKの中からbulkloader.pyというファイルを探して覗いてみました。
【注意】:google_appengineの中には同じ名前のファイルが複数個所にあって分かりにくいですが、ここで言っているのは
\google_appengine\google\appengine\tools
にあるbulkloader.pyです。

この中で定義されているExporterというクラスがLoaderに対応するクラスのようです。さらに見ていくと、appcfg.py(これも上記のbulkloader.pyと同じフォルダ)の中のPerformUploadという関数の近くに(2038行付近)PerformDownloadという関数が定義されています。アップロードには’upload_data’というコマンドが紐付いていますが、ダウンロードには’download_data’が紐づけられているようです(2267行付近)。

つまり、データのアップロードの時に作ったローダクラス(Googleの説明だとalbum_loader.pyになっている)の中のLoaderをExporter、loadersをexportersに書きえてやれば動きそう。
アップロード時のコマンドは(ローカルでテストする場合は)以下でしたが

appcfg.py upload_data --config_file=album_loader.py --filename=album_data.csv --kind=Album --url=http://localhost:8080/remote_api <app-directory>

エクスポートの方では以下のようにすれば動くはず。

appcfg.py download_data --config_file=album_loader.py --filename=exporteddata.csv --kind=Album --url=http://localhost:8080/remote_api <app-directory>

ちなみにローカルでテストするときはちゃんとGAEのローカルサーバ(dev_appserver.py)を動かしておいて下さいね。
いや、しかし動かないんだこれが。
 KeyError: ‘Album’
と言われてしまう。「Album」なんて種類のデータは無いんだと。いや、さっきアップロードしましたよっ!データベースから種類「Album」のデータの最初の100件を表示するテストページも問題なく動作してますよ!

なんでだろー。
知ってる方いましたら教えてくださいー。

結局、日本語を含まない場合のインポートだけしかできませんでした。
データのエクスポートができないままでは新規利用者にとって本格導入したい人も二の足を踏みます。もし他のプラットフォームに移行したくなっても、データが取り出せなきゃどうしようもないので。Googleさん、早く正式対応してください。それからドキュメントを…orz

Amazon EC2とGoogle App Engineの比較(それぞれの特徴をまとめてみる)

どうも、うしねずみです。

最近クラウドサービスをちょこちょこ触っています。Amazon EC2とGoogle App Engine(GAE)を両方触ってみたので、それぞれの特徴をまとめてみようかなと思います。

Amazon EC2

【長所】:自由度が高い

Amazon EC2の特長は何と言ってもその自由度の高さです。EC2ではユーザは「使いたいインスタンスのスペック」、「簡単なセキュリティポリシー」、「使いたいインスタンスの数」を選んで、インスタンスを起動します。例えば、OSがWindows Server 2003、CPUの性能はSmall(SmallとかHighとかで選ぶ)、HTTP(80番ポート)とリモートデスクトップ(3389番ポート)を使えるようにして、1インスタンスを起動、といった感じです。インスタンスというのは、PC1台だと思うとわかりやすいと思います。起動したインスタンスにはリモートデスクトップで接続できて(詳細は以前の日記を)、ソフトのインストールからセキュリティの設定までかなり自由にできます(Linux系OSならSSH接続)。

【短所】:面倒が多い

長所の裏返しですが、アプリケーションを実際に公開するまでの敷居が高いです。最初はWebサーバもメールサーバも何も入っておらず、すべて自分で設定しなくてはならないからです。私のように「Apachの設定はちょっとやりたくない。。。」と言う人にはかなり大変です。

また、負荷が増えた時にスケールすることがGAEに比べると以下の二つの点で面倒です。

  • スケールさせるべきかどうか自分で決めなくてはならない
  • スケールさせる際の追加の作業(とオプションの課金)がある

例えば1つのインスタスでサービスを始めたとします。だんだん人が増えてきて、「どの時点でインスタンスを2つに増やすか」は自分でサーバの負荷などを見ながら決めなくてはいけません。毎日ログを見るとはいえ「もう一台必要かなー?どうかなー?」みたいな判断を自分で行う必要があります(負荷に応じて自動的にインスタンスを追加起動する有料オプションがあったような・・・)。

また、もう1つインスタンスを増やすことにした場合「ロードバランサの設定」という追加作業が生じます。ロードバランサとは、一つのサービスを複数のサーバでこなすための「仕事振り分け」をする機器です。例えばusinezumi.comというドメイン(に紐付いているIPアドレス)でサービスを公開しているとします。2台目のPCにこれと違う新しいIPを割り当てるわけにはいかない(そしたらアドレスは違うけど全く同じ別サイトになる)ので、一つのIPに対して飛んできたリクエストを、それぞれのマシンの負荷を見ながら適当に振り分けていく必要があります。これをやってくれるのがロードバランサです。

EC2でロードバランサの設定が大変かどうかは分かりませんが(おそらくそんなに大変ではない)、追加作業が発生しない方がいいのは明らかです(設定でトラぶったらサービス停止するし)。また、ロードバランサはオプションなのでそれに対して新たな課金が発生するのもあまり喜べません(課金体系は覚えていないのでAmazonのサイトで確認して下さい)。

GAE(Google App Engine)

【長所】:簡単!

GAEの特徴は、公開のまでの簡単さとスケールのしやすさです。GAEでは前回の投稿でも書いたとおりに、非常に公開までが簡単です。もちろんEC2のようにWebサーバの設定など必要ありません。コードをアップロードすればすぐにアプリが公開できます。Webアプリの開発者からすれば、Webアプリ以外のことを特に考えなくてよいのでとても楽だと思います。

また、自分のサービスの人気が出て負荷が増えた時に、自動的にスケールしてくれます。EC2で気にしたような「いつ?何台追加?」みたいなことは全く気にする必要はありません。というかそもそも「何台?」という概念自体がありません。

【短所】:自由度が低い

これも長所裏返しなのですが、自由度が低いです。といってもWebアプリを公開することに関して大概のことはできると思うのですが、「じゃあSubversionをインストールして、開発用のレポジトリサーバにできるの?」っていうとできません。新しいソフトをインストールするとか、どのポートを開放するとか言ったことはできません。できるのは、「コードをアップロードして公開する」だけです。あ、ちなみに静的なファイルのアップロードはできます。

【その他】:データベースの特殊性

これは長所でも短所でもあるのですが、GAEのデータベースはGoogleが使っているものと同じ「Bigtable」というものです。これはリレーショナルデータベースではないという点で、現在使われている多くのデータベースと異なります。これは他のプラットフォームからのGAEへの乗り換えの障害になりかねないのですが、このBigtableの特殊性がGoogleの強力な検索能力を実現していると思えば非常に魅力的に見えてきます。

この件について解説している記事もあると思いますのでここでは詳細は書きません。

まとめ

Amazon EC2とGAEはある程度は競合相手であり、ある程度は競合になりえないと思います。なぜなら「できること」の範囲が違うからです。Amazon EC2ではその自由度の高さゆえ、Webサーバ以外の用途にも使えます。例えばメールサーバや、シミュレーションなどの一時的な計算リソースとして使えたり、何か新しいことを試すためのテスト環境として使うようなこともできるでしょう。一方でGAEでは、アプリ開発以外のことができない代わりに、アプリ開発以外のことはほとんど気にしなくてよいという強みがあります。

同じクラウドというラベルを張られていてもかなり違ったサービスなので、自分が必要としているサービスはどちらなのかをしっかり見極めてから使う必要があるようです。

Page 1 of 3123

Home

Search
Feeds

Return to page top