読者です 読者をやめる 読者になる 読者になる

ボクココ

サービス開発を成功させるまでの歩み

私が実施する Heroku x Rails の高速化をまとめてみた

Herokuの欠点は、Tokyoリージョンがないため、ネットワークによる遅延が気になる と言われている。どの程度による遅延が気になっているのかは人によると思うが、Herokuを最大限に高速化させるために私がやっていることをまとめてみた。

これを実施すれば、Herokuが遅いとはあまり思わなくなると考えている。

f:id:cevid_cpp:20150831200227j:plain

Asset Sync を利用する

[追記(重要)] Asset Syncを利用するのは公式で止めるよう勧告が出ている。CloudFrontを利用する方法が推奨されている。以下の内容は古いのでご注意を。

ーーーーーーーーーーーー

Asset Syncは、Amazon S3に画像やCSS/JavaScriptを置き、各アセットのパスの向き先をそちらにかえてくれるgemだ。これを利用しないと、HTMLとかに置いた静的コンテンツが全てHerokuにアクセスしてしまうため、Herokuサーバーのリソースを無駄に消費しているし、ネットワークが遅いと思われる最大の要因になっている。

デプロイ時にS3へアップロードする時間がちょっとだけかかるが、大して気になるレベルではない。この方法はHerokuのドキュメントでも推奨されている方法であり、Heroku x Rails な環境を運用している人は必ずと言っていいほどすべきことだ。

ファイルアップロードはHerokuを介さない

割と容量の大きなファイルをHerokuサーバー経由でアップロードしようとすると、時間がかかりすぎてHeroku側でコネクションが自動的に閉じられてしまう問題がある。

これが原因でAWSに移したというのはあまりにも勿体無いことだ。

最近は Amazon Cognito と AWS SDK を利用してS3に直接アップロードすることができる。具体的な方法に関しては、以下を参照していただきたい。これでサーバーに負荷のかからない、直接アップロードを実現できる。アップロード後に、そのS3のパスだけHerokuサーバーに送ればよくなる。

サーバーレスでJavaScript だけで画像ファイルをアップロードする方法 | selfree

Amazon Cognito を使わない直接アップロードの方法は以下を参照。 jquery file upload を組み合わせている。

Direct to S3 Image Uploads in Rails | Heroku Dev Center

高負荷な処理は全てバックグラウンドに回す

Herokuはファイルアップロードの問題と同様に、レスポンスを返すのに30秒以上かかると、自動的にタイムアウト(H12エラー)を発する。ということで、処理に時間のかかるコードは積極的に裏でコードを処理させる必要がある。具体的に言うと、HTTPレスポンスはとりあえず早く返して、実際の処理は別のところでおこない、終わったらメールで通知する、などの方法をとる。

最も簡単に対応する方法は、Resque を利用して、処理を後回しで実行させる方法だ。HerokuにはHeroku RedisというHerokuが標準で提供しているRedis環境があるので、手軽にこの処理を実現することが可能だ。

ただ、Webとは別の Workerプロセスという、HTTPリクエストを受け付けるプロセスとは別のプロセスをHeroku側で立ち上げる必要があり、これが料金プランによっては料金がかかる原因になったりする。

それに対応するために、最近私が行っているのは、AWS Lambdaを利用する方法だ。これを利用すれば、ほとんど無料でバックグラウンドジョブを実現できる。普通に Heroku Postgres にもアクセスできるので、Resqueでやっていたことをこちらに移す、ということは十分に可能だと考えている。ただしNode.jsかJavaに限定されるので、Rubyで対応することはできない。

New Relic を利用する

「レスポンスが遅い」という原因の90%以上はそのプログラムの問題だと言えると思う。何か効率の悪い処理の書き方やクエリを書いていたせいで、それに時間がかかってしまうのだ。それがネットワークが原因だと思い込んで、Herokuが遅いと言われてしまっては、Herokuが可哀想である。

f:id:cevid_cpp:20150831200257j:plain

それの原因をつかむためにNew Relicは必ず入れておきたいアドオンだ。これを使ってどのアクセスが最も時間がかかっているのかを簡単に見ることができる。効率の悪いSQLなのか、Rubyプログラムの書き方なのか。運用段階での改善となるが、これも速度改善には大きく役立つ。

Redis を積極活用する

SQLだけで全てをやろうとすると、どうしても限界が来ることがある。例えば最新の記事や注目記事、ランキングなど、これらをSQLで取ってこようとすると、それなりに大変だ。

これらの処理は実は Redisに任せると、かなりの改善が可能だ。RedisはKeyとValueのシンプルなハッシュだと思ってはいけない。Redisのソート済みセット型というのを使うと、自動でソート済みのハッシュを生成してくれる。ソート済みセット型をうまく使うことで、先ほどのような機能を驚くほど高速に実現可能だ。ぜひ調べてみてほしい。ここでもHeroku Redisが活躍することだろう。

API 中心のサーバー設計にする

これはSPA(Single Page Application)のような考え方で、毎回共通レイアウトファイルやCSS/JavaScriptをロードのたびに読み込むのは無駄なので、最初に一気に読み込んで、あとはAjaxを用いて必要な場所だけ部分的に更新していく、という方法だ。

AngularJSやReactなどの流行に行ってもいいし、私の方法のようにjQueryだけで自分で構築することもできる。ちょっとJavaScriptをいろいろいじらないといけないが、高速化だけでなく、ユーザーにとっても「スイスイ動く」と思われるサービスを作ることができる。

TinyPNG を利用する

これは別にHeroku x Rails に限ったことではないが、利用している画像が重いと、それだけでレンダリングに時間がかかり、"重い" と思われてしまう。TinyPNG を利用することで、この速度を改善できる。特にランディングページなどの大きな画像を利用する場合、絶対に利用するべきサービスだ。

Herokuとは関わりはないが、画像の圧縮はレスポンス速度に大きな改善が期待できるため、記載した。

ちょっと告知

そんなこだわりを持って作った "5分で電話を効率化するサービス"「CallConnect」がつい先日、大きく改善してリリースした。ブラウザ上で電話するという新しいかたちを感じてみて頂きたいと思っている。14日間無料トライアル実施中だ。

www.callconnect.jp

終わりに

上記トピックを実践せずに、 Herokuは遅い。 と言われてAWSに早々に移行しまった方は、ぜひ再度Herokuを検討してみてはいかがだろうか。