0

スケール設定してないRubyで書かれたHerokuアプリを止める方法

いままでPassenger+Apacheとか、あらかじめforkさせて複数プロセス待機させておくとかしていたから気づかなかった。
Herokuにお金を払ってwebサーバー複数プロセス起動させているなら問題ないと思います。


1リクエストずつしか処理できない

HerokuでRailsやSinatraを使うときは、Rubyで書かれたwebサーバー(thinとか)が1プロセスだけでそのまま動いているのだが、webrick/mongrel/thinあたりは1リクエストずつ順番にしか処理できない。


つまりこれとかがそうなんだけど、
http://shokai.herokuapp.com/base64img
リクエスト処理中のアプリ自身にリクエストを送ってしまうと固まってしまう。引数にURLを渡されて別のサイトにHTTPリクエストするのを想定して作られているアプリとかに有り得るミスだと思う。

上は画像のURLを渡されるとbase64encodeしたimgタグを作ってくれるページなんだけど、ここにhttp://shokai.herokuapp.comとか入れると固まるのだった。
(今は固まらないようにしてある)

リクエストうける→自分自身にリクエストしてbase64encodeしようとする→1リクエストずつしか処理できないので、待たされる→最初のレスポンスを返せない!!
となる。


対策

上の画像をbase64エンコードするアプリの例
get '/base64img' do
halt 400, 'bad request' if params[:url] =~ /^https?:\/\/#{env['HTTP_HOST']}#{env['SCRIPT_NAME']}/

## 処理続き〜〜
end

こんな感じでリクエスト送る前にチェックしてhaltすると良いと思う。

でもこれだけだとbitly等の短縮URLを使うとすり抜けられる。
どのHTTPライブラリを使うかにもよるけど、自動的に30x系リダイレクト処理をしてくれるライブラリを使う場合はいちいちHTTPヘッダのLocationの指す先を調べないと、HTTPリクエスト処理中の自分自身にHTTPリクエストしてしまう。


ちなみにNode.jsだと複数リクエスト同時に処理できるので全然問題無かった。

1

Node.jsに入門して画像チャットを作ってHerokuで動かした

増井研の合宿があった。

スケジュールに飲み会の時間が書いてなかったので、もしやと思ったら最初からずっと飲んでた。

夕方にgeta6がNode.jsを1時間ぐらいでみんなに教えてくれた。Express+Socket.IOを使って簡単なチャット一歩手前ぐらいのもの。
そこから改造してちくわぶチャットができた。


Tiqav Chat

TiqavのJSONP API使った。
コードはgithubにある


Heroku

デプロイ先のHerokuだけど、よく有料サービスと勘違いされてるけどDBとかを多めに使わない限り無料で使えます。
とくにSinatraやNode.jsに少量のMemcached/MongoDB/PostgreSQLなら無料で使える。俺もまだ金払ったこと無い。

Herokuはレンタルサーバーみたいにパソコンをまるごと借りるんじゃなくて、ソースコードをgitでアップロードすると実行してくれるサービスだよという話も合宿で説明したりした。


Node.jsとnpmインストール

nodeと、nodeのパッケージマネージャをインストールする
brew install nodejs
node -v
curl http://npmjs.org/install.sh | sh
npm -v
MacのLionとHomebrewだと、nodeはv0.6.19が入った。npmもHomebrewにパッケージがあるけどinstallしようとするとnpmjs.org/install.shを使えと言われる。npmは1.1.32が入った。

UbuntuのaptとかSnow Leopardの人とかmacportsの人はバージョンが違うのが入るのがあった。
Node.js 0.7以上のとか使ってる人は後でpackage.jsonのエラーがでたりしてた。
npmも0.2とかが入ったりする人がいたけど、npmjs.org/install.shから入れたらどうにかなってた。


Expressでプロジェクトを作る

WebアプリケーションフレームワークのExpressを使う。
npm install -g express
npm list -g
-gを付けるとglobal(/usr/local/lib/node_modules)にインストールされる。
付けないとカレントディレクトリにnode_modulesというディレクトリが生成される。

mkdir -p ~/src/nodejs
cd ~/src/nodejs
express tiqav-chat
tiqav-chatディレクトリが生成されて、中にMVCフレームワークらしいディレクトリ構成ができる。

cd tiqav-chat
npm install
npmでパッケージ名を指定せずinstallした場合、カレントディレクトリのpackage.jsonの中を見て必要なパッケージがインストールされる。
Rubyのbundlerみたいな機能。


Node起動

node app.js
http://localhost:3000で起動する。


Socket.IOとは

WebSocketやcommetやAjax pollingの中から適当に使える物で接続してくれるすごいライブラリ。


Socket.IOでチャットアプリを作る

インストールする。
npm install socket.io


Expressでテンプレ作った直後と、チャット実装した後との差分を見るとわかりやすい。
implemented chat with socket.io · 1b0edb8 · shokai/tiqav-chat


こういうチャットができる。ブラウザを2つ起動してみると両方で動いている。

app.jsにsocket.ioを読み込んで、on connectionとon postイベントを登録した。
サーバー側はこれだけ。

クライアント側は、まずpublic/javascripts/にjquery置いた。
views/index.jadeにinputタグでボタン作って、views/layout.jadeでsocket.io.js読み込んだ。
で、public/javascripts/main.jsでinputタグのボタンが押された時に、socket.ioのpostイベントにemitでチャットの本文を送りつけるようにした。
あとmain.jsのio.connect(‘http://localhost:3000’)の部分、引数にURL書いてるけど引数無しの方がいい。

動作確認してから、最後にpackage.jsonのdependenciesにsocket.ioを追加した。


Herokuサインアップ

http://www.heroku.com/でサインアップして、メールが来るのでconfirmする。


コマンド入れる
gem install heroku
heroku login
sshの公開鍵がアップロードされて認証される。


NodeアプリをHeroku用に修正する

これが参考になる Getting Started with Node.js | Heroku Dev Center

上で実装したchatアプリをHerokuで動かせるように修正するまでの差分を見るとわかりやすい Comparing 1b0edb8b0fb354b4ea0f0454ae199285cf436455…08a9184e7061bfcfdf061459857e6aead8bfad0d · shokai/tiqav-chat


app.jsのapp.listenを
app.listen(process.env.PORT || 5000, function(){
に修正する。Herokuは起動するプロセスのportは環境変数で指定されるので。


Procfile というファイルを作って、中身にapp.jsを起動する設定を書く
web: node app.js


app.jsを修正する。
HerokuのNode.jsではまだWebSocketが使えないので、ajax pollingだけ使うようにする。
Using Socket.IO with Node.js on Heroku | Heroku Dev Center
io.configure(function(){
io.set('transports', ['xhr-polling']);
io.set('polling duration', 10);
});


package.jsonのenginesにnode0.6系を使うように指定する。
socket.ioもdependenciesに入れておく。


ちゃんと動くか確かめる。foremanから起動する
foreman start
今度は3000じゃなく http://localhost:5000で起動する。


Herokuにデプロイ

するためにgit commitしておく
git init

.gitignore を作って、node_modulesディレクトリ下をcommitしないようにする
node_modules

git add ./
git commit -m 'implemented chat with node.js+socket.io'


Herokuにアプリ登録する
heroku create --stack cedar
これでgit remoteにherokuが登録される


deploy
git push heroku master
heroku open
もうHerokuで動いてるはず。


アプリ名はrenameできる
heroku apps:rename 好きな名前

0

HerokuでSinatra+Memcached使う

Memcached使ってみたかった。なんかキャッシュしなきゃならんので
ブラウザ <---> Heroku <---(cache)---> Twitter
なものを作った。

できたもの http://twiticon.herokuapp.com/

twitterのユーザーアイコンをHTMLに簡単に貼れるやつ。ソースコードはここ

こういうHTMLでアイコンがでる。
<img src="http://twiticon.herokuapp.com/shokai">


小さいのや大きいのも貼れる。

テキストや http://twiticon.herokuapp.com/shokai.txt
JSONでも取得できる http://twiticon.herokuapp.com/shokai.json


使い方は http://twiticon.herokuapp.com に詳しく書いてある。


Twitterのアイコン

ここにAPIがある
GET users/profile_image/:screen_name | Twitter Developers
ユーザーが新しいアイコンをアップロードする毎にURLが変わる。
例えば今の俺のアイコンは https://si0.twimg.com/profile_images/2328443341/tmp_normal.png になってる。


Memcached

ふつうのSQL DBみたいにHDDにデータは保存しないけど、メモリ上で高速に動作する。
あとExpireする期限を決めれる。今回は保存したアイコンURLは12時間で消滅するようにした。
12時間1秒経過すると、再度Twitter APIを使ってURLを取りに行く。


Herokuでmemcached使う

Memcache | Heroku Dev Centerにドキュメントがある。
Herokuのmemcachedはユーザー名とパスワードによる認証があって、SASLというプロトコルを使っているので対応しているgemじゃないと使えない。
dalliというpure rubyのgemが推奨されている。

いつも使ってるCで書かれてるmemcached gemはSASLサポートしてるって記述があるんだけど、使い方がドキュメントに書いてないし面倒臭くなってdalliに落ち着いた。


Herokuにmemcached addon追加した。無料だけどクレジットカード番号登録しないと使わせてもらえない。
% heroku create --stack cedar
% heroku addons:add memcache:5mb


パスワードとかは
% heroku config
で見れる。
MEMCACHE_PASSWORD, MEMCACHE_SERVERS, MEMCACHE_USERNAMEがそれ。
これがHerokuで動かしてるアプリの環境変数に入るので、ENV[‘MEMCACHE_SERVERS’]とかで取り出す。


dalliでMemcached使う

認証なし
require 'rubygems'
require 'dalli'
cache = Dalli::Client.new 'localhost:11211'

Heroku用
cache = Dalli::Client.new ENV['MEMCACHE_SERVERS'], {:username => ENV['MEMCACHE_USERNAME'], :password => ENV['MEMCACHE_PASSWORD']}


で、あとはcacheにset/getすればよい。
# cache 1 hour
cache.set('icon_shokai', 'https://si0.twimg.com/profile_images/2328443341/tmp_normal.png', 3600)

# get icon
puts cache.get('icon_shokai')


ちなみにHerokuのmemcachedは自分のローカル環境からも接続できる。
addons:add memcachedしたらすぐに起動するので、手元で開発してるプログラムで
cache = Dalli::Client.new 'xxxxx.ec2.northscale.net', {:username => '123456heroku.com', :password => 'asdf122345hujiko'}
とか書いても動かせる。太平洋横断してるから遅いけど。


ローカル開発環境にMemcachedをインストール

homebrewやapt-getでインストールできる。
% brew install memcached
% memcached -vv -p 11211
これでlocalhost:11211で起動する。


SinatraでMemcachedを使う

ふつうに上に書いたのを組み合わせてredirectするだけなのでgithub見ればわかると思う。
https://github.com/shokai/twiticon


controllerでは
redirect icon user, size
とだけ書いて、iconって関数は適当にキャッシュもしてくれるように書かれている


あと開発時はローカルのlocalhost:11211のmemcachedを見て欲しいので、環境変数が無かったらconfig.ymlから同じ値を探すようにしたらはかどった。


感想

memcache無料の5MBぐらいだと、tmp_cache gem使ったほうが設定もいらないし楽なのでは・・・

0

Herokuで秋月電子の新商品RSS作った

Nokogiriで取得してRSS作った http://shokai.herokuapp.com/feed/akizuki.rss

Yahoo Pipes版が動かなくなってたので作りなおした。
意外と重要な部分だけ抜き出すの面倒だった

RSS読まれるたびに秋月にアクセスすると申し訳ないのでcacheを作って、20分はcacheを使うことにした
memcacheを使えばいいんだけどなんとなく車輪の大発明をしてしまった。


昔は秋月RSSあったんだけどサイトリニューアルした時に無くなったんだよね。4年ぐらい前だったっけ?


電子工作の勉強をはじめたときは、何か作ろうとしても何が作れるのかわからない。
とりあえず有名な店の新着商品を3ヶ月ぐらい見ていれば、世の中にどんな部品が存在しているのかわかってくるはず。

このへんを定期的にチェックしておくと良い