Tuppari入門中。Tuppariでchatを作ってみました。

@hakoberaさん謹製のTuppariを使ってchatを作ってみました。
Tuppari de chat
yosuke-furukawa/tuppari-de-chat · GitHub

Tuppariというのは、東京Node学園で紹介されたPusherのクローンで、WebSocket通信用に作られたサービスです。

Tuppariの詳細はhakoberaさんのこちらのエントリを参考にしてください。
Tuppari - WebSocket on Your Cloud - - Scalaとlift のはずだった ・・・

node.js版ですごく簡単にできました。せっかくなので、作るまでにやったことをまとめてみます。

tuppariインストールからアカウント作成まではこちらを読んでください。

node.js + express + tuppari作成


前準備


expressからサンプル・アプリケーションを作成してください。

$ express tuppari-de-chat

依存関係&tuppari インストール

$ cd tuppari-de-chat
$ npm install
$ npm install validator
$ npm install tuppari

app.js修正


tuppariを使えるようにします。app.js側は本当に簡単で、20〜30行位追加しただけでTuppariに送信できました。

app.js

/**
 * Module dependencies.
 */

var express = require('express')
, routes = require('./routes')
, http = require('http')
, Tuppari = require('tuppari')
, keys = require('./keys')
, sanitize = require('validator').sanitize;
var app = express();

var tuppari = new Tuppari(keys);
//これでchannel作成とchannelへの参加を実施。
var channel = tuppari.join('chat');

app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(express.favicon());
    app.use(express.logger('dev'));
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
    app.use(express.errorHandler());
});

app.get('/', routes.index);

app.get('/chat', function(req, res){
    var message = sanitize(req.query.message).entityEncode();
    if (message) {
        //メッセージを送信。
        channel.send('your_event', message, function (err, res, body) {
            if (err) {
                console.error(err);
            }
            if (res) {
                console.log(res.statusCode, body);
            } else {
                console.error("no response.");
            }
        });
    }
    res.json(null);
});

http.createServer(app).listen(app.get('port'), function(){
    console.log("Express server listening on port " + app.get('port'));
});


keys.js作成


keys.jsにTuppariのアプリケーションID等を入れておきます。

keys.js

module.exports = {
  applicationId: process.env.TUPPARI_APPLICATION_ID,
  accessKeyId: process.env.TUPPARI_ACCESS_KEY_ID,
  accessSecretKey: process.env.TUPPARI_ACCESS_SECRET_KEY
};

あとでherokuにアップする際にはインストール時に得られたアプリケーションID等をprocess.envに入れてください。

$ heroku config:add TUPPARI_APPLICATION_ID=xxxxxx
$ heroku config:add TUPPARI_ACCESS_KEY_ID=yyyyyy
$ heroku config:add TUPPARI_ACCESS_SECRET_KEY=zzzzzz

ローカルで実行する場合は以下のようにして実行します。

$ export TUPPARI_APPLICATION_ID=xxxxxx
$ export TUPPARI_ACCESS_KEY_ID=yyyyyy
$ export TUPPARI_ACCESS_SECRET_KEY=zzzzzz

この方法の方がスマートかも。

クライアント側


tuppariのAPPLICATION_IDを持つクライアントを作成し、app.jsで登録した'chat' channelをsubscribeします。
chat.js

function chat() {
    var message = $('#message').val();
    console.log(message);
    $.ajax({
        url: "/chat",
        data: {message: message},
        async: false,
        success: function(data){
            $("#message").val('');
            //do nothing.
        }
    });
    $('#form').submit(function() {
        return false; 
    });
}
var client = tuppari.createClient({
    applicationId: '6545f512-301b-4951-b36f-a750716cb3a1' // Replace this with your Application ID.
});
var channel = client.subscribe('chat');
channel.bind('your_event', function (data) {
    var date = new Date();
    $('#list').prepend($('<dt>' + date + '</dt><dd>' + data + '</dd>'));
});

あとはindex.jadeやlayout.jadeなんかはこんな感じです。
index.jade

extends layout

block content
  h1= title
  form(id='form')
    input(type='text', name='message', id='message')
    button(type='submit', onClick="chat();") chat
  hr
  dl(id='list')

layout.jade

doctype 5
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
    script(type='text/javascript', src='http://cdn.tuppari.com/0.1.0/tuppari.js') 
    script(type='text/javascript', src='/javascripts/jquery-1.7.min.js') 
    script(type='text/javascript', src='/javascripts/chat.js') 
  body
    block content

使ってみた感じの感想


簡単に使える良い感じのライブラリですね。
クライアントへのpush通知はこれでいいんじゃないかな、という気になります。
Fluentdとかと連携したりして、リアルタイムにリソース監視とか夢が広がります。
ハッカソンもあるので、もう少し入門してみます。

Node.js × GeekTool で ももクロ天気予報アプリ 作りました。

f:id:yosuke_furukawa:20120722231444p:plain

最近コードの匂いがしないエントリばかり書いていたので、危うさを感じ、作ってみました。
#といっても全然書いてないんですが。

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)


また、最近上の本を読んで一度Restfulなアプリケーションを作ってみようと思ったことにも起因しています。
この本の感想は次のエントリに書きます。

とりあえず、ももクロ天気予報アプリ、名付けてmomoclo-weatherを紹介します。

紹介


momoclo-weatherはその日の天気に合わせてももクロちゃんの表情が変わるだけのアプリです。

晴れ:笑顔
曇:むくれ顔
雨:泣き顔

な感じです。2012年7月22日、現在、東京は雨なので、東京なら泣き顔が出ます。

momoclo-weatherの中身は expressとnode-yqlだけです。
クライアントはGeekToolで実行するので、天気予報をYQLから取得して、ExpressでJSONだけを返す、という事を実行しています。

GeekToolを入れていない人にもなんとなく、感覚をわかってもらうためにWeb用のアクセスも用意してます。
momoclo-weather

使い方


GeekToolを入れてください。
GeekToolのシェルに以下のように入れます。

curl --silent "http://momoclo-weather.herokuapp.com/weather.json?member=XXXXX&location=YYYYYY" | grep -E 'momocloimage' | sed -e 's/"momocloimage"://' -e 's/"//' -e 's/"//' | xargs curl --silent -o /tmp/weather_momoclo.png

memberのXXXXXにはmember名を入れます。
kanako, ayaka, reni, shiori, momokaのいずれかを入れてください。

locationのYYYYYにはweatherIDを入れます。
weatherIDはここで検索すれば分かります。
東京ならJAXX0085
横浜ならJAXX0099
という感じです。

例:東京でれにちゃん推しの場合:

curl --silent "http://momoclo-weather.herokuapp.com/weather.json?member=reni&location=JAXX0085" | grep -E 'momocloimage' | sed -e 's/"momocloimage"://' -e 's/"//' -e 's/"//' | xargs curl --silent -o /tmp/weather_momoclo.png

例:横浜でかなこ推しの場合:

curl --silent "http://momoclo-weather.herokuapp.com/weather.json?member=kanako&location=JAXX0099" | grep -E 'momocloimage' | sed -e 's/"momocloimage"://' -e 's/"//' -e 's/"//' | xargs curl --silent -o /tmp/weather_momoclo.png

後はweather_momoclo.pngをGeekToolのimageで出せばOKです。

もしも明日の天気予報をして欲しい場合はURLに&day=tomorrow を入れれば明日の顔が取得できます。

curl --silent "http://momoclo-weather.herokuapp.com/weather.json?member=kanako&location=JAXX0099&day=tomorrow" | grep -E 'momocloimage' | sed -e 's/"momocloimage"://' -e 's/"//' -e 's/"//' | xargs curl --silent -o /tmp/weather_momoclo.png

あとは、JSONから返ってくる値を色々加工すれば、色んな事ができます。冒頭にあった吹き出しで喋らせるような感じにもできます。

天気模様、Sunnyとか、Cloudyとかを出力する場合:

curl --silent "http://momoclo-weather.herokuapp.com/weather.json?member=shiori&location=JAXX0085" | perl -pe 's/,/,\n/g' | grep -E 'text' | sed -e 's/"text"://' -e 's/"//' -e 's/",//'

気温を出力する場合:

curl --silent "http://momoclo-weather.herokuapp.com/weather.json?member=ayaka&location=JAXX0085" | perl -pe 's/,/,\n/g' | grep -E 'temp' | sed -e 's/"temp"://' -e 's/"//' -e 's/",/C/'

と、こんな感じで出来ます。

コード紹介


実際に頑張っているのはYQLです。
YQLはYahooが作ったWebサービスで、SQLライクなQueryを食わせればYahooサービスの結果を取得できます。
やろうと思えばFlickrから画像取得とかも出来るっぽい。

さて、Node.jsにもYQLにアクセスするためのライブラリがあり、node-yqlです。
installの仕方等、詳しいことは以下のURLを見てください。
derek/node-yql · GitHub

コレを使って天気を取得するコードが以下の通り

//Tokyo : JAXX0085
//Yokohama : JAXX0099
//単位はCelcius固定。Fahrenheitがいいって人がいたら、'unit':'f'。
YQLWeather.prototype.executeQuery = function(area, callback) {
    yql.exec("SELECT * FROM weather.forecast WHERE location=@area AND u=@unit", function(response) {
        var validated_res = validate(area, response);
        callback(validated_res);
    }, {'area':area, 'unit':'c'});
};

多分、weather.forecast以外のテーブルも色々あるので、触ってみるのも面白そうです。
GitHub
yosuke-furukawa/momoclo-weather · GitHub

Herokuに置いたRedisをコマンドラインから使用する

前回の記事でMongoHQとRedis To Goを性能比較してみました。
今回はそれを実行している時に発見したTipsを紹介します。(Herokuを使っている人には常識なのかもしれませんが。)
MongoHQにはHeroku上のデータにウェブからアクセスできる管理画面があるのに対して、Redis To Goにはありません。

どうやってデータ管理するのかというと、'heroku' コマンドのプラグインとしてredis-cliへ接続する方法があるので、それを使用します。

Redisインストール


redisをインストールします。ダウンロードページからダウンロードしてmakeすれば実施できます。
Redisインストール済みで redis-cliへのパスが通っていれば、このセクションは読む必要ありません。

コマンドラインから実行する場合は以下のようにしておきます。

$ wget http://redis.googlecode.com/files/redis-2.4.13.tar.gz
$ tar xzf redis-2.4.13.tar.gz
$ cd redis-2.4.13
$ make

もしも Windows 環境の場合は以下のダウンロードページからダウンロードしてインストールできます。
Downloads · dmajkic/redis · GitHub

インストールできたか確認するためには以下のコマンドを実行してください。

$ src/redis-server

[38875] 06 May 13:12:21 - DB 0: 1 keys (0 volatile) in 4 slots HT.
[38875] 06 May 13:12:21 - 0 clients connected (0 slaves), 1005520 bytes in use

こういう結果が出れば、ひとまずRedisインストールは成功です。

redis-cliへのパスを通しておきます。
デフォルトは[ダウンロードされた先]/src/redis-cli にあります。

以下のやり方で通しても良いみたいです。

$ sudo cp redis-server redis-cli redis-benchmark /usr/local/bin/

詳細はココを参考にしてください。
Redis To Go

Download – Redis

heroku-redis-cliをインストールする


以下のコマンドでインストールできます。

$ heroku plugins:install https://github.com/rapportive-oss/heroku-redis-cli.git

herokuコマンドのバージョンが古いとエラーになる可能性があるので、(自分の環境では一度エラーが出ました)上のコマンドでエラーになったらupdateしてから最実行してください。

$ gem update heroku

インストールに成功したら、デプロイしているアプリケーションのフォルダへ移動してください。
以下のコマンドが実行可能になっているはずです。

$ cd <application_root>
$ heroku redis:cli

実行すると、redis-cliと同じコンソールが使えるようになります。
また、他にも以下のコマンドが実行可能になります。

heroku redis:monitor - 現在Redisで実行されている操作がリアルタイムで表示される
heroku redis:info - Redisのリソース状況表示

もしかしたらもっと簡単な方法があるのかもしれませんが、これでRedisの状況が見れたり、直接データが操作できるのは嬉しいです。
詳細はrapportive-oss/heroku-redis-cli · GitHubを参照してください。

ももクロスライダーで測る Redis vs MongoDB on Heroku 第二弾

f:id:yosuke_furukawa:20120504112310p:image

今日はみどりの日らしく、ももクロスライダーも緑の話が活発です。

さてさて、前回に続いてRedisの話です。
危険なほど速いと言われていますが、実際の実力はどうなのか気になるところです。
確かに他の参考記事を見ると、かなり高速なようです。
redis、それは危険なほどのスピード|サイバーエージェント 公式エンジニアブログ
FreeBSDで,mongoDB V.S. Redis - なぜか数学者にはワイン好きが多い

じゃーって事で、RedisとMongoDBをベンチマーク比較してみました。
結論から言うと、Redisはやっぱり鬼のように速いです。

ベンチ方法


RedisとMongoにtwitpicから取得してきた ももクロの画像に関するデータを入れておき、それを取り出す処理がどちらが早いかだけを計測しています。更新系の操作も高速なようですが、ももクロスライダーは今は更新系の操作がないので、これで計測しています。

ももクロスライダーを少し拡張し、

  • /directで実行するとtwitpic API直接呼出してJSON情報取得
  • /redisで実行するとredisからJSON情報取得
  • /mongoで実行するとmongoからJSON情報取得

できるようにしてから計測しています。

一応URLを貼っておきます。アクセスしてみれば、それぞれなんとなく性能の差が分かるかもしれません。
twitpic直接呼出し
redis呼出し
mongo呼出し

このURLに対してローカルサーバーへのアクセス(ホストをlocalhostにして)で計測したものとHeroku上で計測したものの2パターンとさらにアクセス高負荷で計測したものと低負荷なものの4パターンで計測しています。

計測ツールはJmeterを使っています。
低負荷は10秒間以内に10クライアントからのアクセスを測定。
高負荷は10秒間以内に1000クライアントからのアクセスを測定しています。

ベンチマーク結果

■ローカル環境で計測(低負荷:10秒間で10クライアントから接続される)

local(低負荷) direct(twitpic api直) redis mongo
response time 2421ms 3ms 11ms

twitpic APIを直接呼び出ししたときは比較にならないほど遅かったので、省いてグラフ化するとこんな感じです。

ミリ秒なのであまり差はありませんが、redisの方が若干早いですね。圧巻なのはこの次。

■ローカル環境で計測(高負荷:10秒間で1000クライアントから接続される)

local(高負荷) direct(twitpic api直) redis mongo
response time N/A 2ms 3531ms

twitpic APIはもはや受け付けてもらえず、エラーになってしまいました。
Redisはここでも超高速で、ほとんど低負荷の時と速度に差がありません。
mongoは平均3.5秒程度になってしまいました。mongoはメモリ上にデータはないので、ディスクアクセスになってしまい、負荷がかかるとその分、低速になります。

予想をはるかに上回るRedisの速度で、危険なほど速いというのも分かる気がします。

■Heroku環境で計測(低負荷:10秒間で10クライアントから接続される)

heroku(低負荷) direct(twitpic api直) redis mongo
response time 958ms 292ms 252ms

Heroku環境から計測してみると、ネットワークを介しているせいで結構大きな差が生まれています。
twitpicがローカルよりもかなり速いのが気になりますが、とりあえず、置いておきます。(Herokuサーバーに距離が近いのかな?)
redisとmongoだとここではmongoの方が若干早いですね。ただ、双方ネットワークがボトルネックなせいでローカルと比較してかなり遅延している事がわかります。

■Heroku環境で計測(高負荷:10秒間で1000クライアントから接続される)

heroku(高負荷) direct(twitpic api直) redis mongo
response time N/A 4985ms 5664ms

高負荷で計測してみると、ここでもredisの方が早いという結果が出ました。
ただし、ローカルで計測した時よりも全然Redisの恩恵は得られていません。
ネットワークがボトルネックなせいもありますが、Redis to goのコネクション制限がボトルネックになってきていると思われます。Herokuのデータストアとして、add-onであるRedis to goとMongoHQを使用していますが、Redis to goはフリープランでは10コネクションまでしかサポートされていないので、一度に2コネクション使用するももクロスライダーでは5アクセスしか同時に並列で使えません。

それを裏付けるグラフもとれたので紹介します。

この折れ線グラフを見て下さい、青がRedis、赤がMongoです。1000のリクエストを平均するのではなく、1つ1つ描画するとこのグラフになります。横軸がリクエスト発行順番、縦軸がレスポンスタイムです。1〜300番目のリクエストくらいだと早いのが分かります。
500〜600番目のリクエストまではRedisの方が優っていますが、徐々にコネクションがとれなくなり、Redisのレスポンスが下がることが分かります。
最終的な1000番目のリクエストには22秒くらいレスポンスに要していました。
Mongo HQのコネクションに関しては調査不足で分かりませんが、多少安定してレスポンスを返している所から、少なくとも10よりも大きく取られているように感じます。

ここから言えることは、ある程度クライアントが少ない間はRedis To Goの方が良いです。
600クライアント同時接続程度であれば5秒もかからずに返しています。600クライアント以上から同時に接続されるようなケースでは、Redis To Goのプランを見直すかMongo HQの方が良いかも知れません。

まとめ


Redisはやっぱり高速でした。3点まとめです。

  • ローカル環境の単純なアクセスではRedis は爆速。
  • Heroku環境でもネットワークがボトルネックになるもののかなり高速を維持する。
  • クライアントの同時接続数が増えるとRedis To Go アドオンのコネクション数が頭打ちになるため、場合によってはMongoよりも遅くなる。

RedisとMongoだと特質が違うのでもちろん一概に性能だけを比較するものではありませんが、人気を二分するデータストアとしてベンチした甲斐はあったのかと思います。他にもHerokuにはMongo labとかのアドオンもあるので、気になるところです。また、memcached等、他のインメモリKVSと比較するのもいいかも知れませんね。

ももクロスライダー作ったよ。(Redisを使ったアプリをHerokuに置く) 第一弾

f:id:yosuke_furukawa:20120502224348p:image

この前のNode学園でRedisを使ったアプリが多いのと、社内勉強会でRedisの話が出てきたので、試しにRedis使って何かアプリで実践してみようと思いやってみました。

Redisというのは危険なほど速いと噂のインメモリKVSです。memcachedと同じような感じですが、永続化ができること、memcachedよりも高速であることpub/subという通知機能がある事が特徴です。

あと、せっかくだから最近ハマっているももクロの画像を流すアプリでも作ってやれ、という下心もプラスして作りました、「ももクロスライダー」

ももクロスライダー

yosuke-furukawa/momoclo-slider · GitHub

ももクロスライダーの中身は Node.js + Redis + socket.ioになっています。
twitpicから #momoclo の付いた画像を持ってきて、Redisへ一定間隔でセット、せっかくなので、pub/subで新しい画像があったらクライアントへsocket.ioを使って送信までしてみました。特に画像を毎回見に行かなくてもそのまま流しておけば勝手に新しい画像を取ってきてくれるような仕組みになっています。

今回の話はももクロスライダーの中身の話を交えつつ、RedisをHerokuで使う時の注意点を伝えられるといいかなと思います。

■ app.js

/**
 * Module dependencies.
 */
var express = require('express')
  , routes = require('./routes');
var twitpic = require('twitpic').TwitPic;
var redis = require('redis');
var io = require('socket.io');
var redisClient, subscriber;
var port = process.env.PORT || 3000;

// Herokuに置く場合、REDISTOGO_URLがSETされているので、その時は以下のようにする。
if (process.env.REDISTOGO_URL) {
  var rtg   = require("url").parse(process.env.REDISTOGO_URL);
  redisClient = redis.createClient(rtg.port, rtg.hostname);
  subscriber = redis.createClient(rtg.port, rtg.hostname);
  redisClient.auth(rtg.auth.split(":")[1]);
  subscriber.auth(rtg.auth.split(":")[1]);
} else {
  redisClient = redis.createClient();
  subscriber = redis.createClient();
}

var app = module.exports = express.createServer();

// Configuration

redisClient.on("error", function(err) {
  console.log("Error! " + err);
});
subscriber.subscribe('images');

var keyword = 'momoclo';
app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
  // setIntervalで1分間隔でtwitpicを検索。
  setInterval(function() {
  try {
  console.log("keyword = " + keyword);
  twitpic.query('tags/show', {tag: keyword}, function (data){
    if(data) {
      for (var index in data.images) {
        existsAndSet(data, index, keyword);
      }
    }
  });
  } catch (e) {
    console.log(e);
  }

  }, 60000);
  });

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes
app.get('/', routes.index);
app.get('/img', function(req, res){
  redisClient.hvals("images", function (err, images) {
  console.log('redis connect.');
    if (!images) {
      console.log("images is null");
      getImages(res);
    } else {
      var imageData = [];
      for (var index in images) {
        imageData.push(JSON.parse(images[index]));
      }
      res.json({title: keyword, data: imageData});
    }
  });
});

//存在していたらsetしてpublishします。
function existsAndSet(data, index, keyword) {
 redisClient.hexists("images", data.images[index].short_id, function (err, res) {
    if (err) {
      console.log(err);
    } else if (res == 0) {
      if (data.images[index].message.indexOf(keyword) >= 0) {
        var jsonData = {
          category: 'twitpic',
          short_id: data.images[index].short_id,
          message: data.images[index].message,
          username: data.images[index].user.username,
          width: data.images[index].width,
          height: data.images[index].height
        };
        redisClient.hset("images", data.images[index].short_id, JSON.stringify(jsonData), redis.print);
        redisClient.publish('images', JSON.stringify(jsonData));
      }
    } else {
     //KEY EXISTS, DO NOTHING 
    }
  });
}

function getImages(res) {
  console.log("method getImages begin");
  var imgJsonArray = new Array();
  try {
  twitpic.query('tags/show', {tag: keyword}, function (data) {
    if (data) {
    for (var index in data.images) {
      var jsonData = {
        category: 'twitpic',
        short_id: data.images[index].short_id,
        message: data.images[index].message,
        username: data.images[index].user.username,
        width: data.images[index].width,
        height: data.images[index].height
      };
      imgJsonArray.push(jsonData);
      redisClient.hset("images", data.images[index].short_id, JSON.stringify(jsonData), redis.print);
    }
     res.json(
     { title: 'Express1',
       data: imgJsonArray
     });
    }
  });
  } catch (e) {
    console.log(e);
  }

}

io = io.listen(app);
//subscriberが受信したら、その内容をsocket.ioでemitします。
subscriber.on('message', function(channel, message) {
  console.log('getMessage: ' + message);
  io.sockets.emit('updated', JSON.parse(message));
});

app.listen(port, function(){
  console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
});

Herokuに置くときの注意点はココです。

// Herokuに置く場合、REDISTOGO_URLがSETされているので、その時は以下のようにする。
// Herokuの場合
if (process.env.REDISTOGO_URL) {
  var rtg   = require("url").parse(process.env.REDISTOGO_URL);
  redisClient = redis.createClient(rtg.port, rtg.hostname);
  subscriber = redis.createClient(rtg.port, rtg.hostname);
  redisClient.auth(rtg.auth.split(":")[1]);
  subscriber.auth(rtg.auth.split(":")[1]);
} 
// Herokuじゃない場合
else {
  redisClient = redis.createClient();
  subscriber = redis.createClient();
}

REDISTOGO_URLにauth用の文字列も格納されているのでそれを使ってredisのクライアントの認証を済ませておくことがポイントです。
Redisライブラリによっては面倒なurlのparseをしなくてもいいところがあるみたいですが、ひとまずnode_redisライブラリではこの様にします。

コマンドでは以下のようにしてデプロイします。

$ git init
$ git add .
$ git commit -m "slider appli."
$ heroku create --stack cedar momoclo-slider
$ heroku addons:add redistogo:nano
$ git push heroku master

redistogo:nanoであれば無料で5MBまでですが使えます。

最近では、このRedisをセッション管理に使ってsocket.ioをスケールアウトするのにも使えるみたいですね。
自分も試してみよう。

次回はRedisじゃなくてMongoの場合、twitpicから取得してくるだけの場合でベンチ比較してみようかと思います。

play2.0でアプリをherokuにデプロイするまでの道のり

f:id:yosuke_furukawa:20120325204341p:image

先日、四則演算で10を作るプログラムをScalaで書いてみたので、それをwebアプリ(Make10)にしてみました。

今回はPlay 2.0で作ったサイトをherokuにデプロイするまでやった事を簡単に説明していきます。

ちなみに四則演算の問題を前に出していました。
https://twitter.com/yosuke_furukawa/status/181063838932799488:twitter:detail:left

これの答えは以下の通り:
7,8,8,9の答え
4,4,6,6の答え
4,4,6,7の答え
4,5,5,9の答え
5,5,5,7の答え
3,4,7,8の答え
1,1,5,8の答え

さてさて、冒頭の通り、今回Play Framework 2.0でWebアプリ(Make10)にしてみました。

Play Framework 2.0からScala主体になっている上に色々な機能が追加されています。
Play1.0との差異は以下のサイトに書かれています。
API Only - Stack Exchange

Play2.0のアプリをherokuにデプロイするまでやった事を簡単に説明していきます。

まずはwebアプリをローカルで動作するようにします。

$ play new make10

これでひな形アプリが作られます。

これだけだと何でもないアプリなので、変更をしていきます。
まず自分はEclipse派なので、eclipseプロジェクト化を行います。

$ cd make10
$ play

と実行してください。
その後、

[make10] $ eclipsify

と実行するとeclipseプロジェクトにしてくれます。

そこからeclipseへ既存プロジェクトのインポートをすればeclipseプロジェクトとして実行できます。
※このやり方だとviewsに変更を加えても即座に反映はされないようです。Eclipseでエラーになってもブラウザで見るとエラーになっていない事が多いです。この辺りはScala IDE側の問題としてPlay teamは説明しています。
Documentation: IDE — Playframework

Application.scalaに変更を加えていきます。
一応何の変更をしたかを書いておきます。

Applicaiton.scala

package controllers

import play.api._
import play.api.mvc._
import core.Make10
import play.api.templates.Html

object Application extends Controller {
  
  def index = Action {implicit request =>
  taskForm.bindFromRequest.fold(
    errors => Ok(views.html.index("Please input 4 digit numbers")),
    label => {
      val array = label.split(' ').toList.map { _.toInt }
      if (array.length != 4) {
        BadRequest(views.html.index("error!! please input 4 digit numbers."))
      } else {
        val list10 = Make10.make10(array)
        println(array)
        println(list10)
        if (list10.size >= 1) {
          val answerList = list10.map{_._2}
          Ok(views.html.answers("Please input 4 digit numbers", new Html(answerList.mkString("<li>10 = ","</li><li>10 = ", "</li>"))))
        } else {
          Ok(views.html.index("The answer is nothing. " + label))
        }
      }
    }
  )}
}

変更したらサーバーを走らせてみます。

[make10] $ run

localhostに9000でアクセスすれば結果が得られます。

完成したらherokuにデプロイします。


まず、herokuにデプロイするためにProcfileをプロジェクトのルートフォルダに作ります。
Procfile

web: target/start -Dhttp.port=${PORT} -DapplyEvolutions.default=true

その後、プロジェクトのルートフォルダで以下のコマンドを実行します。

$ git init
$ git add .
$ git commit -m "init"

gitが入っていない場合はインストールしてください。

gitでのコミットが終わったら、以下のherokuコマンドを実行します。

$ heroku create --stack cedar

これでherokuにアプリケーションのプラットフォームができます。

$ git push heroku master

これでherokuにpushされます。

こんな感じでデプロイできます。
デフォルトで作成される名前が気になるようならrenameもできます。



さて、これでwebアプリになったので、どんな問題が出ても解けるね。
9 9999 9999 99999
とかも解けるね( ̄ー ̄ )

English Tweet Botの英語追加ページを若干アップグレード。

English Tweet Bot
・English Tweet BotのページにあったEditボタンとdeleteボタンを実装しました。
・追加した人以外が編集/削除してもエラーになるようにしました。
Botのコメントに#English, #eigoのハッシュタグをつけるようにしました。

あまり大きな変更ではないですが。

デザインも刷新していきたいですね。
これを使ってみようと思います↓
Twitter Bootstrap

追記:ちょっと使ってみました。
かっこよくなったのはいいのですが、少し薄くて見にくいような・・・。