heroku

2014年4月24日 (木)

WebSocket通信のメリットを考える

何故だか昨日のエントリが意外なほど読まれている。(^^;;;


Twitterでの言及も過去最大級かも。特に、


サーバーとの通信はAjax併用でも良いんですが、多分全部WebSocketでやる方がシンプルだし速度面やセキュリティ面でも優位があります。

 

この文章に引っ掛かった人が多いようで、これについてもうちょっと掘り下げてみます。


★ Ajaxの代替としてのWebSocket

そもそもの話として、素のWebSocketではリクエスト(この場合クライアント→サーバのメッセージの意)とレスポンス(サーバ→クライアント)を対応付けることができません。これを実現するためには自力でサーバ/クライアントの双方にメッセージをハンドリングするための仕組みを実装しならず、それは結構めんどうな作業です。

今回、その部分をフレームワーク化しようとしているので、それが実現できた場合のメリットを考えてみます。
ざっと思いつくのは以下です。

  • KeepAliveかつ、httpヘッダのパースも不要になるので速い
  • 特に断続的なリクエストが大量にある場合大きな優位となる(実験結果)
  • 冗長構成でサーバが複数台ある場合も単一クライアントからのリクエストは常に同一サーバに送信される
  • 単一接続からのリクエストは常に同一クライアントからのリクエストであることが保証される


速度メリットは実はおまけみたいなもので、重要なのは最後の2つです。これ、言い換えればWebSocketをAjaxの代替として使うことでステートフルな通信が可能になるということなんです。

既存のWebサーバーではリクエストルーティング機構のインスタンスはサーバー単位で1つなので、リクエスト処理の流れは

  1. リクエスト受信
  2. Cookieを使用してユーザーを同定
  3. 必要ならMemcache等からステート情報を取得
  4. リクエストを処理


のようになりますが、WebSocketの場合接続毎にリクエストルーティングのインスタンスを作ることができるので、2、3の手順がまるまる不要になります。

リクエスト処理のパフォーマンスを出すためになんらかのキャッシュを行う場合もオンメモリだけで間に合いますし、Cookieやhiddenパラメータを使って情報を引き回すことも不要になります。

# ただし、意図しない切断はありえるので長時間ステートを保持することや常にステートが維持されていることを前提としたプログラミングをすることはお勧めしません。この辺のベストプラクティスはまだ確立されていないので当面は自分のバランス感覚だけが頼りになります。(^^;


★ WebSocketとセキュリティ

前述の通り、WebSocketでは単一接続からのリクエストは同一クライアントからのものであることが保証されます。

近年のJavaScriptプログラミングでは処理全体をdocument#readyのクロージャで括るのが一般的ですが、その場合仮にXSSの脆弱性があったとしても外部から接続済みのWebSocketインスタンスにアクセスすることは不可能です。

つまり、最初の接続時にクライアントの確認をしっかりと行っておけば以降のリクエストは攻撃ではないと見なすことができます。なのでCSRFのチェック等は不要です。

問題は接続時の確認を、どのように行うかですがこれはこの資料が参考になります。

http://www.slideshare.net/muneakinishimura/webhtml5-31749532

ほとんどの場合はくログイン認証自体は従来のhttp(s)接続で行ってその後はCookie併用でクライアントを同定することになると思います。
http接続とws接続が同じオリジンであればこれで十分なはずです。


次節で紹介するWebSocketにはSame Origin Policyがないことを利用したスケールアウトを行う場合はもう少し考慮が必要になります。


★ WebSocketとスケール

とりあえずルームモデルでも単純な水平スケールが可能であることは確認済みです。


ただ、この方法には効率の悪い部分があります。

というのは、サーバ100台に対して100人のユーザが1つのルームに入室した場合、最悪100人全員が異なるサーバに接続する可能性があるからです。
ユーザがばらけるとバックエンドのRedisの負荷が上がるので同じルームのユーザは1台とは言わないまでも極力小数のサーバに集中してほしいのです。

HerokuやELBなどフロントにロードバランサがいる場合、こうしたリクエストを適切にふりわけることはできません。(パス毎にリクエストを振り分けるロードバランサがあれば、可能ですがあまり筋の良い解決方法とは思えません。)

が、実はもっと簡単な解決方法があります
WebSocketにはSame Origin Policyがないので単純にサーバを分けてしまえば良いのです。

ページをリクエストするホストが「http://www.quizar.info/...」だったとしても、その中でws接続するホストは「ws://room1.quizar.info/...」であっても良いわけですね。

この場合、接続時のクライアント認証をどうするかが課題になりますが、Cookieのドメインを明示するとかバックエンドキャッシュを使うとかいくつか方法はあると思います。(必要に迫られたらちゃんと考えます。(^^;)

このアーキテクチャを採用する場合、同一のコードベースから簡単に複数のアプリケーションを作成できるHerokuは最強のWebSocketプラットフォームだと思いますが、実際にやって良いかどうかはまた別の話です。(少なくとも課金を減らす目的でやるのはアウトだと思います。実際に必要になったら問い合わせますが、パッケージ買いでその範囲で行う分には問題ないだろうと思います。)


★ 課題

最大の課題はやはり切断時の対処です。HerokuもELBも無通信状態が長く続くと切断されますし、スマホの場合スリープやブラウザから別アプリに切り替えることも当たり前の操作なのでそれも考慮しなければなりません。また、Herokuを使う場合はデイリー再起動への考慮も必要になります。

この部分もフレームワークで吸収できればと思っていますが、もうちょっと試行錯誤が必要なのでそれはまたおいおい。(^^;

□□□□
以上、現時点でのWebSocketに対する考察でした。
WebSocket。既存のhttp通信の延長で考えている人が多いと思いますが、うまく使えば劇的なパラダイムシフトが発生するかもしれません。

どこに辿りつくのかわかりませんが、もうちょっとこの道を進んでみたいと思います。(^^;;;

2014年4月22日 (火)

WebSocketアプリのモデルを考える

ども。  
Salesforce Hackチャレンジに参加していたQuizarは安定の選外でした。。。 
ま、Salesforce1まったく関係なかったから無理もない。(^^;  

さて、それはさておきQuizarの製作を通してWebSocketを使えば今まで世の中に存在しなかったようなアプリを作れる可能性があるということを実感したわけですが、現実にはほとんどWebSocketアプリは開発されていません。

こうした状況の最大の原因は世の中にWebSocketアプリ開発のためのモデルやフレームワークがほとんど存在しないためだと思っています。

現状では低レベルのAPIだけはあるものの、それしかないからその上のモノは全部自分で作らないといけないわけです。
Quizarの開発過程でやったことだけでも、メッセージのルーティング、リクエスト/レスポンスの対応付けなど通常のWeb開発ではやらないようなプログラミングをかなり行っています。

状況としてはhttp/1.0の頃にServlet APIだけで開発していた極初期のWebアプリ開発に近いかな?
あるいは、ひと固まりのメッセージがあるだけでそこに何の付帯情報もないということを考えると生ソケットで直接メッセージを読み書きしている状況に近いという気も。(^^;

いずれにせよこの状態から一本のアプリを作り上げるのは結構な労力なわけで、逆に言えば、になんらかのモデルとフレームワークがあればWebSocketアプリ開発のハードルは一気に下がる気がします。

いくつかのアプリ開発を通してなんとなくその形が見えてきはじめているので、今回はそれをざっくりまとめてみます。


★ モデル

多分WebSocketアプリのモデルはほとんどすべてルームモデルに収まります。

「ルームモデル」という言葉は小西の造語ですが、とりあえず以下のように定義します

  • アプリ空間に複数のルームがあり、
  • ルームには複数の人が入室でき、
  • ルーム内にいる人同士がリアルタイムにコラボレーションできるアプリケーション

シンプルな定義ですが、それだけにほとんどのWebSocketアプリはこのモデルにあてはまるはずです。

Quizarは典型的なルームモデルアプリケーションですが、ルームの中にいるのはクイズの出題者と回答者という役割の違う2種類の人達です。
このようにルームに入室した各人の役割は複数あり得るので

  • ルーム内にはロールの異なる複数の人が存在して良い


というのも定義に加えて良いかもしれません。


★ フレームワークに必要な機能

次にルームモデルアプリケーションを作成するために必要な機能を考えてみます。
サーバー側にルームを管理する機能が必要なのはもちろんですが、最低限メッセージのフォーマットを決めておかないとリクエストのハンドリングもままなりません。
また、クライアント側も必然的にSPA(Single Page Application)となるのでWebSocket以外にも多くの機能が欲しくなります。

ここではとりあえずメッセージ、サーバー、クライアントの3つにわけて必要と思われる機能を列挙してみます。(細かく説明し始めるとあまりにも長くなりそうなので。。。)

ちなみにこのリストもQuizarの開発経験からの逆引きです。

● メッセージ

  • JSON形式のエンベロープとする
  • リクエスト毎にIDを持つ
  • リクエストのコマンドが識別できる
  • 任意複数のパラメータが渡せる
  • レスポンスのエラーが識別できる
  • JSON、HTML、Textなど複数の形式のレスポンスを返すことができる


● サーバー

  • 複数のWebSocket接続を束ねるルームを持つ
  • ルームの永続化(RDB等へのルーム情報の保存)機能は範囲外
  • スケールアウトするための外部のPub/Sub機能を利用できる
  • ルーム内ユーザーへのメッセージブロードキャスト
  • ブロードキャストメッセージのフィルタリング
  • メッセージのルーティング
  • メッセージのロギング


外部のPub/Subは今のところRedis以外に知りませんが、差し替えられるように抽象化はした方が良さそうです。

● クライアント
クライアント側ではルームモデルに限らずSPA全般で必要となる機能も含みますが、それぞれの機能は独立して使えるモノとします。

  • WebSocketをAjaxライクに使用できる(リクエストに対応するレスポンスを取得できる)
  • メッセージのエラーハンドリング
  • リクエストにひもづかないメッセージのルーティング
  • サーバーサイドからの切断時の再接続
  • アイドル状態の識別とイベントハンドリング
  • プラグイン可能なデバッグログ
  • ポーリング
  • WebStorageでのテンプレート管理
  • PushState対応


サーバーとの通信はAjax併用でも良いんですが、多分全部WebSocketでやる方がシンプルだし速度面やセキュリティ面でも優位があります。(これはそのうち別に書きます。)

逆に面倒な部分としては切断時の対応とデバッグ(ブラウザのWebコンソールでメッセージを見ることができない)があるんですが、これらをうまくフレームワーク側で吸収できればメリットだけを享受することもできるはずです。

□□□□
とりあえず、こんなところでしょうか。
なんか忘れてる気もしますが、まぁそれはおいおい(^^;

実際にはQuizar開発当初からフレームワーク化を意識していたので、上に書いた内容は概ね実装イメージがあります。(もちろんブラッシュアップや再考が必要な部分も多々ありますが。。。)

サーバーサイドはWebSocketとRedisのPub/Subが使えれば何でも良いんですが、とりあえずPlayで。(他でも作るとしたら多分次はNode.js)

というわけで、6月末を目途にフレームワークとなんか適当なルームモデルアプリを作ります。(^^v



2014年4月 8日 (火)

OpenSSL祭り(CVE-2014-0160)

ども。
本当はC10Kの後篇を書こうと思ってたんだけど世間はOpenSSL祭りなので、それに乗っかったエントリを書いてみる。

なお、これから書く内容はあくまでも小西の個人的な理解なので、正しいという保証はどこにもありません。

言っちゃなんですが、セキュリティとか暗号とかはどっちかと言うと苦手分野です。(^^;;;

しかし、書くことによって自分の理解が進んだり新たに気付くことがある。という経験則があるので、とりあえず書いてみることにします。

いつにもまして、ツッコミ歓迎


★概要

http://heartbleed.com/

にまとまってます。
ざっと僕の理解(くどいようですがあくまで個人的理解)をまとめると以下のような感じです。(上記サイトに書いてあることだけでなく、他のサイトで見た内容も含まれています。)

  • OpenSSLに深刻な脆弱性が見つかった
  • この問題はSSL/TLSプロトコルの問題ではなくあくまでOpenSSLの実装の問題
  • 過去2年間のOpenSSLリリースすべてにこの脆弱性が存在する(1.0.1以降)
  • この脆弱性を利用するとSSL通信を行っているホストのメモリを読むことができる(一度に読めるのは64Kまでだが、繰り返し何度も実行することができる)
  • OpenSSLを使っている著名なWebサーバとしてはApacheやnginxがある
  • 問題のあるバージョンのOpenSSLがバンドルされているOS多数


。。。ダメじゃん。。。(--

コンピュータ上のすべての処理は一度メモリを経由するわけで、それを考えるとSSL証明書とかクライアントから送信したユーザー名とパスワードとかクレジットカードの番号なんかまで全部どこかのタイミングでメモリに載るわけで、可能性としてはそれら全部を盗むことができるわけです。

。。。ホンマかいな。。。(--

 

★サーバがJavaなら大丈夫?

いいえ、そんなことはありません。
クライアントから直接Java製のサーバに繋いでいるのであれば大丈夫なはずですが、

  • フロントにApacheがいる
  • ELBをhttpモードで使っている
  • Herokuを使っている


全部アウトです。
Apacheは言わずもがな、Herokuのフロントエンドはnginxのはずですし、ELBも問題があることがアナウンスされています。

ロードバランサに脆弱性がある場合、攻撃者が読めるのはロードバランサのメモリまででバックエンドにあるアプリサーバのメモリは読めません。

しかし、HerokuもELBもSSLをロードバランサがほどいてバックエンドとの通信はhttpで行っていたりするので、そこでの通信内容はメモリに載ります。

逆に言うと

  • 一度も通信に載らないHerokuの環境変数などが漏洩することはない(言語は関係ない)
  • ELBをTCPモードで使用している場合は問題ない


と思ってるんですが、今のところ誰からも同意を得られていません。。。

。。。ホントにそうか???。。。

★ベンダの対応状況など

ネット上の情報によるとAmazon Linuxはパッチがでたようですね。

http://qiita.com/tachiba/items/83e5fd31d06e6577abb3

ELBはまだのようです。

Herokuの対応状況はstatus.heroku.comで確認できますが、SSLアドオンが影響受けるので対応終わったら証明書を再発行しろとか書いてあります。

https://status.heroku.com/incidents/606

。。。マジでか。。。(--

Heroku Postgresを再起動するというメールも来てるらしいですが、そういえばPostgreSQLとの通信もSSLでした。。。(--

 AWSとかSalesforceのアクセスキーなんかを全部Regenerateしろ、とか言われそうでかなり憂鬱。。。(--

 (4/9 追記)

例えばAWSのアクセスキーが環境変数に設定されている場合、Herokuルーターのルートからそれが外部に漏洩しなかったとしても、肝心のAWSサーバーに脆弱性があれば、やっぱり再生成が必要なわけで、結局のところアクセスキーの類は全部再生成が必要な気がします。


★その他個人としての対応など

サーバー側の心配もありますが、個人で使用しているアカウント類の心配もした方が良いのかもしれません。脆弱性のあるサーバにログインするとそのユーザー名、パスワードを盗まれている可能性があるわけで。。。

パスワードを変更した方が良いとは思うんですが、今それをやってもそのパスワード変更リクエストが盗まれる可能性があるので、どないせーっちゅーねん。。。(--

とりあえずはベンダーからのアナウンスがあるまではアクションを起こすべきではないんじゃないですかね。。。

おいおいGoogleとかEvernoteとかからパスワードを変更しろっていうメールが届き始める気がしますが、絶対フィッシングとかしかける奴がいる気がするので、ドメインとかちゃんと確認した方が良いですよ。


心の底から思うんですが、誰か間違ってると指摘してください。(--

続きを読む "OpenSSL祭り(CVE-2014-0160)" »

2014年4月 4日 (金)

HerokuのWebSocketでC10Kに挑戦(前篇)

前回SalesforceハッカソンにWebSocketのクイズアプリを出してきたよ~という話をしたわけだが今のところ身内以外からはほとんどアクセスされてないっぽい。
まぁほとんど宣伝してないからそれは別に構わないんだけど、審査されている形跡もないのは大丈夫なのか。。。(^^;


さて、それはさておきWebSocketアプリを作ったら是非試してみたいと思っていたことのひとつにC10K問題の検証と言うのがあります。
C10Kとはクライアント1万台問題の略で平たく言うと「WebSocketってクライアントとずっとソケット繋ぎっぱにするわけだよね。そんなのクライアントの数がちょっと増えたらあっという間に破綻するんじゃね?」という問題のことです。


★検証シナリオ

今回作ったアプリにはルーム毎にチャットの機能があるのでそれを利用することにします。
具体的な目標数値としてはとりあえず以下のように設定しました。

  • ルームを100個用意する
  • 各部屋に3秒に1人ずつ、100人(100クライアント)入室する(最大時同時接続1万クライアント)
  • 各クライアントは5秒に1回チャットメッセージを投げる(合計100万メッセージ)


とりあえず1万クライアント。全員を同じルームにいれちゃうとチャットのブロードキャストがえらいことになってしまうので、ルームだけは分けることにします。

3秒とか5秒とか段階的に負荷を増やしているのはWebSocket接続はだいたい1接続が1人の人間に対応するので、人間の操作としてそこまでの連続リクエストはこないだろうと思われるからです。(先にDevSumiで行ったデモは特殊な例です。(^^;)

この数字をもう少し細かく見ていくと以下のような負荷になります。

  • 最大時秒間2000メッセージ(1万人が5秒に1回メッセージ送信するから)
  • その時のブロードキャストメッセージが20万件(1メッセージはルーム内の100人にブロードキャストされるから)
  • 1クライアントが1秒間に受信するメッセージは20件(ルーム内の100人が5秒に1回メッセージ送信するから)


。。。やりすぎ?。。。

しかし、普段負荷テストをする時はJMeterでぶんぶんリクエスト投げてるわけだから、3秒に1人追加とか5秒に1回のメッセージとかもの凄く手加減してる気分なんだが。。。。(--

それに1クライアントあたり秒間20件のダウンロードがあると言っても、チャットのメッセージなんか1000個あってもそこらのHTMLページのサイズよりも小さいだろうから、通信のコスト自体はどう考えてもhttpアプリのJMeterテストよりも低いはず。
そもそもWebSocketアプリの負荷テストのセオリーがわからないし、テストツールも自作するんだからとりあえずやっとけやっとけ。

Herokuはこの程度の負荷ではおかしくならない!(多分)


★とりあえず1dynoでテスト

いきなり高負荷のテストをしようにもどの位のDynoを用意すれば良いのかまったく見当がつかないのでまずは1dynoの臨界点を見極めることにします。

手始めに

- 1ルームに100人入室、100メッセージ送信

というテストをしてみたところ、何の問題もなく正常終了しました。
まぁNon-Blocking I/Oを使っているんだからこれくらいはやってもらわないと、という感じです。(^^;
ちなみにこの時点でテスト環境で使っているRedisはRedisCloudの無償版です。無料枠でもこの程度のリクエストは捌けるってことですね。

ここから人数とメッセージ数はそのままでルームを増やしていきます。



- 2ルームに100人入室、100メッセージ送信

まだ何のエラーも起こりません。
メモリ使用量も200MB未満なのでかなり優秀です。
Play(というよりもNettyとAkkaのActor)はかなり筋が良いんだろうと感じました。


- 3ルームに100人入室、100メッセージ送信

接続できないクライアントがでてきました。
が。。。サーバー側にエラーログが出ていないところを見るとどうやら限界を迎えたのはクライアント側っぽい。
テストクライアントはjava_websocketを使ったPlayアプリケーションとして作っているんですが、このライブラリが内部的に使っているのは通常のSocketクラスなので、この辺がNon-Blocking I/OとNormal I/Oの差でしょうか。またjava_websocketはSocketの監視のために接続毎にスレッドを作っているのでその辺もメモリ使っちゃって臨界が早い原因になっていると思います。

ここから先はテストクライアントもHeroku上で複数dynoを立ててのテストとなります。テストクライアントをHerokuにあげて実行したらこれもクリアしました。

本当に優秀。。。ていうか予想ではそろそろエラーになるはずなのになんで動いてるの?って感じ。。。(^^;;;


- 4ルームに100人入室、100メッセージ送信

ここにきてようやく2種類のエラーが発生しました。

まずそろそろ来ると思っていたRedisの最大接続数到達エラーがでました。
無料枠のRedisの接続数はたったの10個しかありません。RedisClientはコネクションプールから取るようにしていますが同時接続数が増えると当然並列で使われる件数が増えます。また、それ以上に影響が大きいと思われるのはルームの数だけRedisのSubscriberが必要になる点です。4つのルームがアクティブなら4つのコネクションがそれぞれのルームに占有されます。つまり同時接続数は増えているにも関わらず使えるコネクション数は減っているわけでむしろよくここまで持ったと思います。
本番環境ではRedisGreenを使ってますが、この先のテストでは接続数無制限のRedisCloud(2.5GB)を使うことにします。


またHerokuのRouterがH11を返すようになりました。このエラーの意味はDevCenterのドキュメント、Request queueingのあたりに書いてあります。


HerokuのRouterは1dynoに対して50同時接続しか行わずそれ以降はキューに入ります。そのキューがあふれた場合にこのエラーが発生するわけです。
しかし実際には400近くの接続が1dynoで行われています。これが何でかというとルーター自体が複数台あるからです。結果からの推測では1アプリで少なくとも8台ルーター使ってる計算になります。
なので、H11が発生した場合でもリトライすると別のルーターに繋がってあっさりと接続できたりします。(はっきりと裏を取ったわけではありませんが高負荷時にブラウザからアクセスした際にそれらしき現象を確認しました。)

この動作はWebSocketに限らずhttpアクセスの場合も同じはずです。Httpクライアントからのアクセスがエラーとなった場合に直後にリトライするのって意味あるんだろうか?と思ってましたが、少なくともHerokuでは意味があると言えます。(まぁH11が発生しているなら素直にdynoを増やせっていう話ではありますけど。)


★1dynoの限界

ここまでの結果からH11が発生する400前後が限界となります。
ただしサーバー側のメモリ使用量を見ると300MB前後なので、ルーターさえリクエストを回してくれればもっと多くのリクエストを1dynoで捌けるだろうとは思います。

ちなみに2X dynoの場合でもこのルーターの制限値は変わらないようで、これはもうちょっとなんとかならんのか?あるいはWebSocket有効のルーターでは制限値を引き上げられないか?ということをサポートに問い合わせ中です。

どうやら現在ちょうどルーター周りの改修を行っているらしく、うまくいけば近々対応されるかもしれません。(みたいな返事が来ているが実際のところはどうなのか謎。。。)

この結果を受けて1万クライアントを捌くには25dynoくらいだろうと辺りを付けて実際試してもいるんですが続きはまた今度。
これ書きながら追試したいことやサーバーサイドを直したいところも出てきているので。(Herokuに問い合わせ中のモノもあるし)

ちなみに現在のところ、完全にノーエラーで接続できていると確認できたクライアント数は5600台位です。

傾向としては1度接続が確立したクライアントの動作は安定しておりパフォーマンスもほとんど悪くならないようです。(何故ならソケット繋ぎっぱだから。)

負荷がかかってくると接続時にエラーになることが多いようで、そこをなんとかすれば7,8000まではすぐに行くんじゃないかという気もしてます。(1回のテストで2GB以上のログが出るんで雑な検証しかしてませんが。。。(^^;;;)

後篇がいつになるか(あるいは本当に書かれるのか?)は未定です。(^^;;;

2014年1月17日 (金)

Heroku Meetup #11

昨夜はHeroku Meetup #11。

新年会と銘打っただけのことはあってご飯もお酒もいつもより増量されてて凄い楽しかったです。

日本酒持ってうろうろしている女子が複数いたのには笑った。(^^;

自分のやったLTの資料はこちら

http://www.slideshare.net/shunjikonishi/heroku-tips1

小ネタ集です。(^^;

LTは予定通り途中で切られたわけだが、他の人見てると最後までやりきる人も多くて、最初から全部喋る気のなかった自分をちょっと反省した。(--

また、小ネタがたまったらLTにもトライしようと思います。

2014年1月14日 (火)

HerokuでスケーラブルWebSocket

前回の続きです。
前回Play+Redisでスケーラブルに動くWebSocketアプリケーションが完成したので今回はそれを実際にHeroku上で動かしてみます。

といってもCLIで「heroku labs:enable websockets」を叩く以外は特に変わったことをする必要はありません。
この辺は以前にも書いたのでそちらも参照してください。

http://blog.flect.co.jp/labo/2013/12/herokuwebsocket-aad6.html

実際のところはデプロイすれば特に何の問題もなく動きます


★HerokuでWebSocketアプリを動かす場合の留意点

Herokuの特性と照らしてWebSocketアプリを動かす時に気をつけなければならない点がいくつかあります。
ざっと思いつくところは以下です。

  1. 通信が無い状態が30秒続くと接続が切れる
  2. マルチDyno対応
  3. デイリー再起動がある


1番目の30秒ルールへの対応はクライアントまたはサーバから定期的にPing的なメッセージを送信することで回避できます。

2番目のマルチDyno対応は通常のスケールアウトと何ら変わるところがないのでRedisを使うことでクリアです。

やっかいなのは最後、再起動対応です。
HerokuのDynoManagerはWebSocket接続がある場合でも容赦なく再起動をかけてくると思います。(試してませんが。。。)
これはHerokuの内部仕様なのでユーザ側では制御不能です。

以下、これに対してどういう対策が可能かを考えてみます。


★接続遮断時のクライアントの動作

Dynoの再起動をローカルでシュミレートする方法は単純に起動中のPlayframeworkをCTRL+Dで止めるだけです。

この時にクライアント側のJavaScriptがどのような動作になるかをまずは検証します。検証の方法は単純にWebSocketの onopen/onerror/oncloseイベントにconsole.logを仕込むだけです。

検証に使用したブラウザはChrome, Firefox, IE10です。
サーバ強制終了時に発生するイベントとその順序は以下です。

  • Chrome: onclose
  • Firefox: onclose
  • IE10: onerror, onclose


IEのみonerrorが発生していますが、これは多分IEの問題はまたPlayのWebSocket実装の問題です。強制終了に限らずサーバ側からWebSocketを切断(EnumeratorでInput.EOFを送信)した場合、常に発生するので。

ざっと検索した感じ(Playの話ではないですが)この辺りが関連トピックかなと思いますが詳しくは調査していません。

onerrorイベントは発生していますが、例外でスクリプトが停止する等の実害はないのでここでは気にせず話を進めることにします。

重要なのは強制終了による切断であってもクライアント側からは単にサーバからWebSocketを切断されただけに見えるという点です。(PlayはShutdownHookでクリーンアップを行っているのでそれも関係あるかもしれませんが)


★oncloseで再接続してみる

安易ですがoncloseイベントが発生した場合に再接続してみることにします。

ローカルのテストでは手動で再起動を行っている関係で、切断直後はまだサーバが起動していません(※1)。当然接続はエラーになりますが、その場合以下の順序でイベントが発生しました。

  • Chrome: onerror, onclose
  • Firefox: onerror, onclose
  • IE10: onerror, onclose


すべてのブラウザでonerrorとoncloseが発生しています。
onopenとoncloseって必ず対で発生するとは限らないんですね。。。イマイチこれが実装依存なのか正規の仕様なのかがW3Cの仕様見てもわからないんですが、とりあえずoncloseを拾って時間をおいて数回再接続を試みるような実装はできそうです。(※2)


※1 HerokuでマルチDynoで動かしている場合は再接続は別のDynoにつながるのでエラーなしで再接続できます。

※2 console.logは見てませんがiOS、Android(のChrome)でもリトライでの再接続ができているので、やはり接続失敗時にoncloseは発生しています。


★Window#onunloadでのoncloseイベントのクリア

ここまでの内容を実装して動かしてみるとローカル、Herokuともにいい感じに再接続ができています。(Herokuでの再接続のテストはCLIで「heroku ps:restart web.2」のようにDyno単位での再起動を行います。)

あともう一つ注意が必要なのはWebSocket#oncloseイベントはブラウザでページを閉じた場合などにも発生するのでその場合には再接続を行ってはいけないという点です。

ここではWindow#onunloadイベントでoncloseをクリアしていますが、ページ遷移以外のWebSocket切断があるアプリでは他にも制御が必要になるでしょう。

最終的なクライアント側のコードはこうなりました。(タグをつけようか迷いましたがmasterの最新です。)

https://github.com/shunjikonishi/websocketchat-redis/blob/master/app/views/chatRoom.scala.html

★まとめ

Dyno再起動に対応した再接続はがんばればできそうです。
しかし、ここをがんばる位なら素直にHeroku以外のプラットフォームを選んだ方が良いように思います。(^^;;;

しかししかし、EC2を使うとしてその場合にサーバの異常終了による予期しない切断の可能性を考慮しなくて良いかどうかは疑問です。
個人的な感覚としてはWebアプリはエラーが発生したとしてもリロードで回復するなら許せるんですが、かなり高い確率でそれもなんとかせぇと言われそうな気がしますね。。。(--

□□□□


まぁ必要に迫られたら考えますが、ふとこんな言い回しを思いつきました。

パフォーマンスもアベイラビリティも金で買ってください。
パフォーマンスは性能正比例で性能2倍になれば価格も2倍ですが、アベイラビリティは1%向上する毎に価格は10倍です!


昔友人が言っていた言葉の焼き直しですが、あながち間違ってないと思いますね。(^^;

2013年12月27日 (金)

PapertrailのWebAPIを使う

前回記事ではHerokuのPlatformAPIを使用してリアルタイムにログを解析してグラフを作ってみたわけですが、やっぱり直近の1500行しかログが取れないとあんまりいい感じのグラフにならないわけです。(--

で、PapertrailにもWebAPIがあったことを思い出してそっちを使ってみることにしました。

 

http://help.papertrailapp.com/kb/how-it-works/http-api

 

ちなみに検索だけだけどラッパーAPIもあります。

 

http://oss.flect.co.jp/libs/ja/papertrailTool.html

 

このラッパー最初に作ったのは多分1年以上前だと思うけど、今見ると色々WebAPI進化してますね。。。

昔は検索以外のAPIなかった気がするけどいつの間にか検索条件の保存とか管理系のAPIもあるっぽい。

まぁ使うことはなさそうだけど、こうしたPaasサービスの提供する機能がなんでもAPIで叩けるようになっているのは良い傾向です。夢が広がります。(^^v

 

★APIでtail的なことを行う

これ非常に簡単です。

まず、検索APIをパラメータなしで実行するとJSONで以下のレスポンスが返ってきます。

  • 含まれるログのIDの最小値
  • 含まれるログのIDの最大値
  • 直近のログ100件(生成日時、プログラム、メッセージなどが構造化されています。)

これに対して指定できるパラメータには以下のものがあります。

  • min_id - 指定された場合このID以降のログを取得する
  • max_id - 指定された場合このIDまでのログを取得する
  • min_time - 指定された場合この日時以降のログを取得する
  • max_time - 指定された場合この日時までのログを取得する
  • q - 任意のクエリ(WebUIと同じ書式)

※min_time, max_timeはUNIX TIMEで指定

 

min/maxの指定でどこから、どこまでを取得するかが指定できるわけですね。ちなみに取得件数は常に最大100件で指定できないようです。

なので、最初にmin_timeを指定していつからのログを取得するかを決めたら、あとはそのレスポンスのmax_idを、次のリクエストのmin_idに指定してループをまわせば連続したログが取得できます。

ここでポイントとなることが2点あります。

 

min_idに指定したIDのログは結果に含まれない

min_idを指定すると指定したIDのログを先頭にしてそれ以降のログが取得されそうな気がしますが、実際には含まれません。

なので、max_idを次のリクエストのmin_idに設定する際には+1するとかの小細工は必要ありません。(PapertrailのログIDは連番ではないので+1しても抜けることはおそらくありませんが。)

 

結果が0件の場合でもレスポンスのmax_idは設定される

直近のログまで読んでしまったら、次のログがまだ存在しないということがありえますが、その場合でもレスポンスには最終行のログIDがmax_idに設定されます。

なので「結果が0件の場合に備えて前回のmax_idを取っておいて。。。」みたいなことをする必要はありません。

 

細かいことですが、この2つのおかげでtailライクなログ取得を組む際のロジックがシンプルになります。

 

★ログ解析アプリケーション

そんなこんなで、ログ解析アプリはPapertrailのAPITokenを入力すれば誰でも使えるようになりました。(^^v

 

http://flect-papertrail.herokuapp.com/

 

Herokuアカウントで使う場合は要求される権限の範囲が広すぎるため、試しに使ってみるのも抵抗がある気がします。開発環境で試してみたいけど本番環境まで一緒に見えちゃうよ!みたいな。

が、PapertrailのAPITokenであれば開発環境で試してみるということもできるので、気が向いた人は使ってみてくださいな。

 

□□□□

どうでもいいけどこのアプリ、最初に作ったのが今年の4月頃。そこからずっと放置してたのがこの数日でグラフ機能を追加。この先もまたしばらく放置の予定です。

これをトラブルドリブン開発(TDD)とでも名づけましょうか。(^^;

それでは良いお年を(^^)/

2013年12月20日 (金)

HerokuとJavaのメモリのお話

Heroku上のJavaアプリでNewRelicで見る限りメモリはまだ余裕がありそうなのにR14(Memory quota exceeded)がログに出力されるようになり、アプリの応答が極端に悪くなる(というより1台のDynoがほぼ無応答になる)という事象が発生したので、原因を調査しました。

結論から言うとエラーの原因は特定できなかった(いや、まぁメモリ不足であることはわかっている(^^;)んですが、なかなか興味深いこともわかったのでここにまとめておきます。

 


★ JavaVMのメモリ管理

Javaで使用中のメモリ量を調べる方法としてはRuntimeのメソッドを使用する方法とMemoryMXBeanを使用する方法があります。

http://docs.oracle.com/javase/jp/7/api/java/lang/Runtime.html
http://docs.oracle.com/javase/jp/7/api/java/lang/management/MemoryMXBean.html

前者はHeapメモリのみを対象とし、後者はHeapとNonHeapの両方を対象としています。
なので、Runtimeからとれる値はMemoryMXBeanからも取得できるわけですが、その関係は以下のようになっています。

  • Runtime#maxMemory == HeapMemorUsage#getMax
  • Runtime#totalMemory == HeapMemoryUsage#getCommitted
  • Runtime#freeMemory == HeapMemoryUsage#getCommitted - HeapMemoryUsage#getUsed


ログ等にメモリ使用量を出力する場合、お手軽なのでRuntimeのメソッドの方が使用されることが多いと思いますが、この方法だとNonHeapの容量が含まれていないことに注意する必要があります。

ちなみに今回テストで使用したアプリ(Play1)ではNonHeapは80MB - 100MB位確保されていました。意外と多いです。

 


★ NewRelicのInstancesページに表示されるメモリ使用量


NewRelic自体もMXBeanを使用して情報収集しているはずなので、ここでの各グラフはMXBeanで取得できる値と対応関係があるはずです。

その対応は多分以下です。

  • Physical = HeapMemoryUsage#getCommitted + NonHeapMemoryUsage#getCommitted
  • CommittedHeap = HeapMemoryUsage#getCommitted
  • UsedHeap = HeapMemoryUsage#getUsed

多分というのは、今回アプリの中からMXBeanの値を定期的にログ出力するようにしてその値をグラフ化するということをやったんですが、そのグラフと自前のグラフでいくらか傾向が異なるからです。

ちなみに自前のグラフはこんな感じ。(1分間隔で出力)

 

Memory

 

見ての通り、UsedHeapがかなりギザギザしたグラフとなっていますが、これは定期的にgcがかかっているためと推測されます。

しかし、NewRelicのグラフではこのようなギザギザは観測されません。
複数Dynoの平均値だからかとも思ったんですが、1Dynoの場合でもこのようなグラフとはならないので謎です。。。(--

ただ全体的には同じようなグラフとなるので、ベースとしている値は上記であっていると思います。。。

が、先にも書いた通り複数Dynoを使用している場合はNewRelicのグラフは平均値になるのでなので各Dynoがどれだけメモリを使用しているかはNewRelicではわかりません

 

あと今回の調査とは直接関係ありませんが、NewRelicのagentのjarファイルはちょいちょいアップデートされているので、たまには見直した方が良いかもしれません。

ほとんどの場合、最初にNewRelicを追加した時にDownloadしたAgentをそのまま使用していると思いますが、2系から3系へのバージョンアップではAgentのオーバーヘッドが減っている、とCHANGELOGに書いてあります。(まぁ実感できるものではありませんが。。。この辺Addonが自動的にやってくれると嬉しんだけれど。)

 


★ heroku labs:enable log-runtime-metrics

labsの機能、log-runtime-metricsを有効にすると20秒に一回各Dynoのメモリ使用量がログに記録されます

 

https://devcenter.heroku.com/articles/log-runtime-metrics

 

ここで出力される「memory_total」がHerokuが監視しているメモリ使用量で、この値が512MBを越えるとR14が出力されます。

ちなみにR14が出力される間隔も約20秒おきなので、このスイッチで切り替わるのはログ出力の有無だけでメモリ監視自体は常に実行されているのでしょう。

ちなみに記録対象はすべてのDynoなので、heroku run bashとかSchedulerで動いたDynoのメモリ使用量も記録されます。

各Dynoのメモリ使用量が個別にわかるし、オーバーヘッドもほとんどないと思うのでlabsだからと敬遠せずに全部のアプリで有効にした方が良いと思います。

 


★ Javaのメモリ使用量とDynoのメモリの関係

さて、今回の調査を始めた元々の動機は「NewRelicで見るメモリグラフではそれほどメモリを使用しているようには見えないのにR14警告が出ることがある(気がする)」というものでした。

先のグラフではわざと載せませんでしたが、log-runtime-metricsの値ももちろんログから拾えるのでグラフ化できます。これらを重ねればJavaのメモリ使用量とDynoのメモリの関係がわかるはずです。

その衝撃の計測結果はテストアプリのでのグラフを公開しているので是非直接ご覧ください。(12月16日以降はグラフが見られます。)

 

https://flect-papertrail.herokuapp.com/app/fexp/metrics/2013-12-19?key=memory_rss,memory_total,HeapUsed,HeapCommitted,Physical,PsRss

 

なにが衝撃だったかって、Herokuの計測しているmemory_totalがJavaのHeapCommitted(+ NonHeapCommitted)よりも小さいことがあるということです。

そんなことってある???

僕の認識ではCommittedというのはJavaによって確保済みのメモリなので、それはプロセスが使用しているメモリとほぼ等しいはずと思っていたんですが、外から見たメモリがそれよりも小さいってどういうこと???

そんなはずはねーと思って、アプリ上にpsを叩いた結果を表示するコントローラを付けてみたところ。。。

。。。マジでかーーー。。。ここでもCommittedよりも小さい値が表示されてるよ。。。(--

誰かこの現象を説明してください。m(_ _)m

 

(12/24 追記)

その後ログに「ps aux」の結果から抜いたメモリ使用量を出力するようにしてグラフに追加したところ、ほぼmemory_rssと重なりました。なので外から見た場合のメモリ使用量はJavaのCommittedメモリよりも大きいことも小さいこともあり、その数字が実際のメモリ使用量(Herokuの監視対象)と考えるのが良いようです。

□□□□

話を戻すとグラフを見るとmemory_totalはJavaから見たメモリ使用量よりも小さい時も大きい時もあります。ただグラフの上がり方を見るとアプリの負荷と相関関係があるのは間違いないです。

アプリ以外にメモリを使っているものというのがイマイチ想像できないんですけど、通信バッファとかもあるんだろうか?psにでてこないプロセスも何かある気がする。(根拠なく言ってます。。。)

これはもうこういうものだと思って飲み込んだ方が良いのかもしれません。。。深入りしても満足のいく結果が得られそうな気がしない。。。(敗北宣言)

ログからDyno毎の正確なメモリ使用量を可視化するツールができただけでもまぁ良しとしますかね。。。(--

 

★なぜか次回予告

さて、今回作成したグラフツールはまぁまぁ便利です。(ドキュメントとかまったく書いてないけど)

仕組みとしてはPapertraiがS3にアーカイブしたログを引っ張ってきて解析しているので、前日以前のログは見られるわけだけどPapertrailのログがアーカイブされるのはだいたいお昼過ぎ(昔はもっと早かった気がするけど)なので、「今R14出てるよ!」みたいな時には使えない訳です。

そこはやっぱりリアルタイムで見たいのが人情!

。。。かどうかは知りませんが。(^^;

 

そういやPlatformAPIで直近のログを取得できたな。。。

 

そういやHerokuってWebSocketサポートしたんだよな。。。

 

じゃリアルタイムグラフも作れるんじゃね?(^^v

 

ということで、次回はHerokuとWebSocketの話です。

2013年12月11日 (水)

SalesforceとHeroku Postgresの同期

こんにちは。このエントリはSalesforce Advent Calendarの11日目です。


ノリでSalesforce Advent Calendarに手を挙げてしまいましたが、僕自身はSalesforceでアプリを作ったことは一度もありませんVisualForceは1時間で挫折しました。

そんな人間がSalesforceのAdvent Calendarで何を語ろうと言うのかというと、SOAP APIのJavaラッパーを作ったりもしているので、それを使ったSalesforceと外部RDBMSとの同期連携について書いてみようと思います。

※タイトルは半分釣りでHeroku Postgresと書いていますが他のRDBでも多分動きます。


★ところで Heroku Connect

本題に入る前にSalesforceとHeroku Postgresの同期と言えば先日のDreamForceでHeroku Connectが発表されました。

http://blogjp.sforce.com/2013/11/heroku1-.html

今のところ予告編だけで実際に試せるのは来年以降になるらしいですが、某エバンジェリストに聞いたところでは、これはSalesforce上のオブジェクトと同じ構造のテーブルをPostgres上に作成して双方向に同期するもののようです。

ちなみにガバナ制限は受けない某エバンジェリストは力強く言っていましたが、別の筋からはそんなの聞いてないという話もあり、真偽は定かではありません。(^^;

素晴らしいんですが、この枠組みだとまずSalesforceアプリありきなので、既存のHerokuアプリのデータをSalesforceに取り込みたい場合は使いにくいかもしれません。

今時のクラウドデータベースではストレージ容量は十分過ぎる程にあるので、アプリで使用するテーブルとSalesforceと同期するテーブルはきっぱり分離してPostgres内でトリガーで同期するというアプローチはアリだと思いますが、自前のテーブルの方はともかく自動生成される同期テーブルにトリガーをつけて良いかどうかは謎ですね。。。
それにこのやり方で完全双方向は無限ループの回避がやっかいかな?

いずれにせよ使えるようになったら試してみるつもりなのでこの話はまたいずれ。



★ところで flectSalesforce

ちょいちょいあるよー、とはこのブログでも言っていたのですがちゃんと紹介したことはなかったと思うので改めて。

http://oss.flect.co.jp/libs/ja/flectSalesforce.html

flectSalesforceはSalesforceのSOAP APIのJavaフルスクラッチ実装です。

僕は元々某所でSOAPのかなり初期からSOAPクライアント/サーバーを作ったりもしていたのでSOAPには色々と言いたいことがあるんですが、それはまぁ良いでしょう。

とにかくAxisは使いたくなかったので自分で作ることにしたんですが多分正解でした。SalesforceのSOAP実装にも色々と突っ込み所があるんですが、これまた誰の共感も得られそうにないので墓まで持っていきます。(^^;

□□□□
前置きが長くなりましたが。。。
そんなこんなでこれはSalesforce APIの実装のひとつです。必要なものから作っているので全部のメソッドを実装しているわけではありませんが、オブジェクトの更新/削除など主要なものは網羅しています。(describeLayoutやdescribeTabsみたいなメソッドを実装しても誰も使うことないでしょうしね)

元々は社内で使うことしか想定していなかったのでコメントも日本語です。(^^;
(必要最低限しか書いてませんが)

既存のAPIラッパーとの相違点としては単純なAPIのラッパーに留まらず、独自のユーティリティインターフェースを備えている点があげられると思います。

  • SQLライクなINSERT, UPDATE, DELETE構文によるデータ更新
  • Fixtureによるテストデータの更新、削除
  • RDBからのSELECT結果をSalesforceに取り込み
  • SalesforceからのSELECT結果をRDBに取り込み


などなど。
後ろの二つがここから紹介する内容です。
APIの使い方自体は

http://flect-salesforce-sample.herokuapp.com/

でサンプル付きで説明しているのでここでは主に処理の枠組みについて説明します。


★RDBからのSELECT結果をSalesforceに取り込み

http://flect-salesforce-sample.herokuapp.com/sqlsync

RDBからSELECTした結果でCSVを作り、それをBulk APIで投入します。

特徴的なのはSELECTしたフィールドとSaleforceオブジェクトのフィールドの対応をSELECT文内のエイリアスで指定するという点です。

 

SELECT COL1 as Col1__c,
       COL2 as Col2__c
  FROM TABLE1
 WHERE ...

 

この仕様によりJoinや関数の使用などDBMSのサポートするすべてのSQL構文を駆使してSELECT文を書くことができます。

同期処理として運用する場合はWHERE句に更新日付で結果を絞る条件を付加して定期的に実行します。(それに特化したアプリもあります。)
項目に外部IDがあればレコードの更新方法はUPSERTになります。


ちなみにBulkAPIには1万行制限がありますがSELECT結果が1万行を超える場合は内部的にファイル分割されるので、1万行を越えても問題なくデータを取り込むことができます。


★ SalesforceからのSELECT結果をRDBに取り込み

http://flect-salesforce-sample.herokuapp.com/sobjectsync

これもBulkでやりたかったんですが、クエリ自体はSOAP APIで投げています。
何故ならBulkクエリでは参照フィールド(CreatedBy.Nameとか)が取れないからです。。。


使えねー。。。(--

サブクエリは仕方がないと思うけど、参照フィールドくらいはBulkでも取らせて欲しいよ。。。

□□□□

仕組みとしては要するにSalesforceに対してクエリを実行した結果をぐるぐる回しながら指定のキーでUPDATE文を実行し、結果が0件だったらINSERT文を実行しているだけです。
QueryMoreは内部的に処理されるので、結果セットが大きい場合も問題なく実行できます。

こちらの方ではSalesforceオブジェクトのフィールドとRDBのテーブルカラムを一つずつ指定する形式をとっています。

なのでSOQLの関数は使用できません。(あるんだっけ?(^^;;; )

その代わりSELECTした結果をJavaで加工した結果をマッピングすることができます。

 

request.addFunctionMapping("NAME", new SObjectSyncRequest.Function() {
    @Override
    public Object evaluate(SObject obj) {
        String fn = obj.getString("FirstName__c");
        String ln = obj.getString("LastName__c");
        return ln + fn;
    }
});

 

引数のSObjectから値を取得してそれをごにょごにょと演算をした結果をreturnすればOK。データ移行の自由度はむしろこっちの方が高いです。


実行結果の詳細なハンドリングを行いたい場合はListenerクラスを組み込むことで個別にエラーとなったオブジェクトをハンドルすることができます。

□□□□
こんな感じで雰囲気は伝わったでしょうか?
Heroku Connectが完全自動で固定的なのに対して、こちらの方は自由度高くプログラムに組み込めるのが特徴と言えると思います。

実際のところ、これらの機能は社内要件から実装したものであって公開して誰でも使えるようにしているのは、もののついでという感はあるのですが。。。(^^;

ご意見、ご要望等あればGithubのIssue、Twitter等に書いてくれれば可能な範囲で対応するので、定常的な同期に限らずワンショットのデータ移行の場合などでも思いだしたら使ってみてくだされ。(^^)/

2013年12月 6日 (金)

Heroku Platform APIのJavaラッパー

しばらく別の仕事をしていたため一ヵ月以上放置していたんですが、Heroku Platform APIのJavaラッパーをリリースしました。

 

http://oss.flect.co.jp/libs/ja/heroku-platform-api.html

 

Platform API自体がまだベータでどのように変更されるかわからないのでバージョンは0.9としています。(というかいくつかFeatureRequestしようと思ってます。)

元々は全メソッドを実装するつもりはなかったんですが、7割ぐらい実装した段階で全部作ろうと決心しました。でも、どう考えても使うことのなさそうなメソッドも多いです。(^^;

使い方は多分PlatformAPIのJavaDocとHerokuのDevCenterのリファレンスを並べて眺めればだいたいわかるんではないかと。

- http://oss.flect.co.jp/apidocs/heroku-platform-api/jp/co/flect/heroku/platformapi/PlatformApi.html

- https://devcenter.heroku.com/articles/platform-api-reference

基本的にはRESTのAPIとJSONを忠実にJavaに落としていっただけです。ちなみにJavaDocは全クラス、メソッドがGitHubのソースへのリンク付きという親切仕様です。(^^;

以下、いくつかポイントを絞ってわかりにくそうなところと突っ込みどころを解説していきます。

 

★認証

APIを使用するための認証方法には以下の4つがあります。(括弧内は対応するPlatformApiクラスのメソッド名)

  • OAuth(fromOAuth)
  • refresh_tokenをaccess_tokenと交換する方法(fromRefreshToken)
  • ユーザー名とパスワードをaccess_tokenと交換する方法(fromPassword)
  • ユーザー名とAPIキーを使用する方法(fromApiToken)

一番基本的な使い方はOAuthでの認証です。OAuthを使用するためにはあらかじめHerokuダッシュボードのAccount画面でOAuthClientを登録してClientIDとClientSecretを取得する必要があります。

このJavaラッパーではPlatformApi#getOAuthUrlメソッドでHerokuの認可画面のURLがとれるので、そこにブラウザでアクセスしてリダイレクトで返ってきたcodeをPaltformApi#fromOAuthに渡せば有効なPlatformApiクラスが取得できます。

OAuth認証のレスポンスには常にrefresh_tokenが含まれています。つまり一度認可を受けてしまえばあとはずっとオフラインアクセスが可能です。

refresh_tokenはユーザがダッシュボードでそのアプリをRevokeしないかぎりずっと有効です。

あとはユーザ名とパスワードまたはAPIKeyを使用してAPIを叩くこともできます。(APIKeyはコマンドで「heroku auth:token」を叩くと表示されます。)

パスワードでの認証はaccess_tokenとの交換が必要なのに対しAPIKeyは交換なしでいきなり使用できます

これが可能なのでPlatform APIは実は全部以下のようにcurlで1コマンドで実行可能です。

 

curl -i -H "Accept: application/vnd.heroku+json; version=3"
    -u <USER>:`heroku auth:token`
    https://api.heroku.com/apps

 

★Scope

OAuthで認可を求める際に指定できるScopeは以下です。

  • global(全権限)
  • identity(アカウント情報のみ)
  • read(アプリの環境変数以外を読める)
  • write(アプリの環境変数以外を書ける)
  • read-protected(アプリの環境変数も読める)
  • write-protected(アプリの環境変数も読める)

アプリの環境変数が見えてしまうとDATABASE_URLやその他のAddonの設定情報も丸見えなのでデータベースの中身まで見ることが可能になるわけです。

そのためにread/read-protectedのような分け方がされているんだと思うんですが、全アプリに対して同じスコープが適用されるのでほとんど有効なバリアとはなりません。

現状、アプリ毎に個別に権限が指定できないのがPlatform APIの一番の問題点だと思います。

ちなみにglobalスコープでも実行できないメソッドもいくつかあります。(OAuthClientやAuthorizationなど。これらのメソッドを実行するにはパスワードかAPIKeyでの認証が必要。。。なんですけどそもそも使い道が思いつかないですね)

 

★Formation

とはProcfileで定義するWebプロセスやWorkerプロセスのことです。(前は「Process」と呼ばれてた気がするけどこれから変わるのか???)

heroku psコマンド相当のことはFormationまたはDyno関連のメソッドでだいたいできます。

たぶんこの辺が今のところ一番実用的なメソッドです。

 

★ LogDrain

LogDrainはDynoのログを外部のSyslogに流す仕組みです。PapertrailやTreasureDataなどのログを扱うAddonはこの仕組みを利用しています。

一覧を取得するとそこにはAddonが使用しているURLも含まれているので、それを別のアプリでaddすると複数のアプリから一つのAddonに対してログを流すことが可能です。

やっていいのかどうかはわかりませんが。。。(^^;;;

でも多分Addon側からはログを流してきたDynoがどのアプリに属しているかを知る術はないと思うんですよね。。。

 

★LogSession

作成するとhttpsのURLが生成されて、そこにGETでアクセスすると直近のログが取得できます。URLは5分間または接続してから1時間有効です。(とドキュメントに書いてあります。)

接続してから1時間???

どういう意味かと思ってたら作成時のオプションで「tail=true」と指定すると、レスポンスはContent-Lengthなしで繋ぎっぱなしになります。

なんつー乱暴な。。。(--

しかし実際にtailっぽく動作するので便利は便利です。。。でもアリなのか?これ?

 

□□□□

その他、releasesの一覧は取れてもrollbackはできないとか、全体的に突っ込みどころ満載な感じはありますがFeatureRequestやバグレポも割とちゃんと聞いてくれて実際に修正されたりもしているので、皆もっとガシガシ突っ込んであげると良いと思います。(^^;

採用情報

株式会社フレクトでは、事業拡大のため、
Salesforce/Force.comのアプリケーション
開発
HerokuやAWSなどのクラウドプラッ
トフォーム上でのWebアプリケーション開発

エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみた
い方、是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、
セールスフォースパートナーです。
heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。
Powered by Six Apart