サンプルアプリケーションの入手とローカルでの実行
それでは実際にHerokuアプリケーションにMemcachedを追加してみましょう。
本連載で作成しているJavaアプリケーションにサーバサイドセッションを使用する簡単なサンプルを作成したので、これを用いてサーバサイドセッションがどのように機能するのか、またMemcachedを使用しない場合どのような問題が発生するのかを見ていきます。
サンプルは筆者のGitHubリポジトリの06ブランチで実装されているので、まずはそちらからコードを取得してください。手順は次のとおりです。
$ git fetch origin $ git co 06 -b origin/06 または、 $ git clone -b https://github.com/shunjikonishi/codezine-sample.git $ cd codezine-sample
今回使用するサンプルの実装コードは次の2つです。
実際の画面はこちらで確認できます。
このサンプルでは画面上で入力した文字列をサーバサイドセッションに保存し、それを画面に表示しています。また、SessionIdとHeroku上で処理に使用されたDynoの番号も同じく画面に表示しています[1]。
まずはローカルで実行してみます。
# Mac $ ./run.sh # Windows $ run.bat
ブラウザでlocalhost:5000にアクセスして、以下のことを確認してください。
- リロードしてもSessionIdが変わらないこと
- 入力したメッセージが画面に表示されること
- リロードしてもメッセージが維持されること
- 別ブラウザで同じ画面を開いた場合にSessionIdが異なること
- ブラウザが異なる場合、他方のメッセージが表示されないこと
このように同一ブラウザからの連続するアクセスで、サーバサイドで値を維持することがサーバサイドセッションの機能です。
[1] ローカルで実行した場合、Dynoは「null」と表示されます。
Herokuで動かしてみる
続いて、このアプリケーションをHerokuで動かしてみます。このサンプルアプリケーションをまだHerokuに登録していない場合は、新たにアプリケーションを作成してからHerokuに06ブランチをpushしてください。
# サンプルアプリケーションをHerokuに登録していない場合 $ heroku create # 06ブランチをHerokuにpush $ git push heroku 06:master
Herokuでもローカル同様にセッションが機能していることが確認できると思います。 1Dynoで動かしている場合、画面上のDynoの項は常に「web.1」となります。
Dynoの数を上げてみる
次にDynoの数を2に上げてみます。
$ heroku ps:scale web=2
この状態で再度ブラウザにアクセスすると、今度はセッションに保存したメッセージがほとんどの場合、正しく表示されません。リロードしながら注意深く画面を観察すると、次のことがわかります。
- Dynoの項が「web.1」になったり「web.2」になったりする
- Dynoが変わるとSessionIdも変わる
- SessionIdが変わるとメッセージはもはや保持されない
リロード時にDynoが切り替わるのは、Dynoが複数ある場合に、リクエストがどちらのDynoで処理されるかがわからないからです。
今回使用しているフレームワークでは未知のSessionId(他方のDynoで生成されたSessionId)が来た場合は、新たにSessionIdを生成し直しているため、Dynoが切り替わるたびにSessionIdも変わります。
まれに入力したメッセージが正しく表示されることがあるのは、メッセージをセッションに保存するPOSTリクエスト後のリダイレクト処理がたまたま同じDynoで処理されたためです。
Memcacheirの追加
次に、複数のDynoでもセッションが正しく動作するようにMemcachedを追加します。
ここでは一番古参のMemcachied Add-onであるMemcachierを使用します。
$ heroku addons:add memcachier
追加したら、heroku configコマンドでMemcachier関連の環境変数が追加されていることを確認してください。
$ heroku config MEMCACHIER_SERVERS: ... MEMCACHIER_USERNAME: ... MEMCACHIER_PASSWORD: ...
次に、アプリケーション側でサーバサイドセッションとしてMemcachedを使用するように改修する必要があります。今回使用しているフレームワーク「Webapp Runner」は、Herokuの中の人が、ServletをHeroku上で簡単に動かすことを目的として開発しているものです。そのため、起動時の引数に指定するだけでMemcachedを使用するように変更できます。
Procfileに「--session-store memcache」というオプションを追加してください。
web: java $JAVA_OPTS -jar target/dependency/webapp-runner-7.0.40.0.jar --session-store memcache --port $PORT target/heroku-sample
この設定により、フレームワークが自動的に環境変数からMECACHIER_XXXXに設定された情報を読みだして使用してくれるようになります。ちなみに、Memcached Add-onとしてMemcachier以外を使用した場合には、手動でMemcachedサーバの情報をheroku configに設定する必要があります。これは最もポピュラーなMemcached Add-onであるMemcachierをWebapp Runnerが特別扱いしているためです。
修正ができたら、git pushしてもう一度動作を確認してみましょう。
$ git add . $ git commit -m "Mod Procfile to use memcachier." $ git push heroku 06:master
今度はDynoが切り替わってもSessionIdは切り替わらず、メッセージも維持されているはずです。
このテストでは一時的にDyno数を2つに上げていますが、テストが終わったら、必ずDyno数を1に戻してください。上げっぱなしにしていると課金されます。
Dynoの無料枠は750時間です。1か月は最大でも744時間(24時間×31日)なので、逆にいえば6時間までは2Dynoで動かしても大丈夫です。
ローカルでの開発
先の変更はProcfileのみだったので、run.sh(run.bat)を用いてローカルで実行する場合に、Memcachedは使用されません。
ローカルでもMemcachedを試したい場合には、次のようにします。
- run.sh(run.bat)に「--session-store memcache」を追加する
- Memcached関連の環境変数をローカルに設定する
Add-onとして追加したMemcachierをそのまま使用するのであれば、次のようにheroku configから値をコピーすることもできます(Macの場合)。
export MEMCACHIER_SERVERS=`heroku config:get MEMCACHIER_SERVERS` export MEMCACHIER_USERNAME=`heroku config:get MEMCACHIER_USERNAME` export MEMCACHIER_PASSWORD=`heroku config:get MEMCACHIER_PASSWORD`
ただし、この場合Memcachierの物理ホストはUS Eastにあるのでローカル(日本)からのアクセスは非常に遅いです。これを回避しようとするとMemcachedサーバ自体も自前でローカルに立てる必要があるのですが、実際のところはローカル環境での開発でもMemcachedを使用する必然性はほとんどありません。ローカル開発では、従来どおりのオンメモリのサーバサイドセッションをそのまま使うのがよいと思います。
おわりに
今回はHerokuでの開発を行う上で非常に重要なAdd-onであるMemcachedを紹介しました。次回はもう1つの重要Add-onであるログ関連のAdd-onを見ていきます。