2011年2月28日月曜日

Access-Control-Allow-Origin

WebブラウザからRESTをアクセスするプログラムを開発するときに、困るのがクロスオリジンの問題。
Webページ上で動作するJavaScriptプログラムは、Webページをダウンロードしたサイトにしかアクセスができない、というアレです。

プログラム開発中では、作ったプログラムをわざわざサーバーに上げるといったような作業が必要となるため、とても不便です。
また、運用時でもREST APIを一般に公開する場合には、大きな制約になります。

この問題は従来JSONPで回避してきましたが、最近はAccess-Control-Allow-Originという手法があるのを知り、g3に実装してみました。


Access-Control-Allow-Originはブラウザ側でのクロスオリジンの選択をするために必要な情報を、HTTPのヘッダにクロスオリジン情報を付加するものです。
ブラウザは、従来はクロスオリジンであればすべて拒否していたのを、ヘッダに許可情報がある場合はクロスオリジンを許可するという動きになります。

具体的には、GET, POST, PUT, DELETEではリプライのヘッダにAccess-Control-Allow-Originヘッダを設定します。

Servletの該当箇所は以下のようになります。(g3の実装なのでScalaです。)

resp.setHeader("Access-Control-Allow-Origin", "*")

GET, POST, PUT, DELETEにAccess-Control-Allow-Originをつけるだけでよいと勘違いしていて、ちょっとはまったのですが、実はOPTIONSでリソースアクセスに対するクロスオリジン定義を返さないといけないのですね。
以下の情報をヘッダに設定します。

Access-Control-Allow-Origin
クロスオリジンを許すURL。すべて許す場合は「*」。
Access-Control-Allow-Methods
Access-Control-Request-Methodに指定されたメソッドを返すのが丁寧っぽいですが、公開してるメソッドをすべてを毎回返すようにしても大丈夫のようです。
Access-Control-Allow-Header
Access-Control-Request-Headersで指定されている文字列を返します。ブラウザがチェック用に使っているみたいです。
Access-Control-Max-Age
このOPTIONSの設定の有効時間。この時間が過ぎると再度OPTIONSでクロスオリジン定義を取りにきます。

Servlet該当箇所は以下のようになります。

resp.setHeader("Access-Control-Allow-Origin", origin)
      resp.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, DELETE, OPTIONS")
      resp.setHeader("Access-Control-Allow-Headers", req.getHeader("Access-Control-Request-Headers"))
      resp.setHeader("Access-Control-Max-Age", accessControlMaxAge.toString)

2011年1月31日月曜日

Web Socketなど

とある事情があって、g3にHTML5のWeb SocketやServer-Sent Event機能を追加してみました。
Web SocketはJettyの機能を利用しています。

今回使ったWeb SocketとServer-Sent Eventを使ったg3アプリケーションは以下のものです。

class Html5Service extends G3Application {
  port("/") html(<p>OK</p>)

  port("/sse") agent {
    case _ => {
      EventStream(System.currentTimeMillis.toString, 1000)
    }
  }

  port("/ws/chat") agent {
    case msg: Post => {
      new Post("/chat", msg.content)
    }
  } invoke("wschat")

  websocket('wschat, "/chat")
  websocket('wstimer, "/timer")

  timer("") agent {
    case msg: Post => {
      Post("/timer", "Time: " + System.currentTimeMillis)(
    }
  } invoke("wstimer")
}

動作概念図はこんな感じ。


「/」(e.g. http://example.com/demo/)にアクセスが来ると「OK」が表示されます。これは動作確認のため。

「/sse」(e.g. http://example.com/demo/sse)にアクセスが来ると、以下のようなServer-Sent Eventを返します。MIMEタイプはtext/event-streamです。

retry: 1000

data: 1292615603413

実現方式は簡単で、新たにtext/event-streamなメッセージEventStreamを追加しました。Server-Sent Eventの仕様はシンプルなので、サーバー側の仕組みもいたってシンプルです。
ただ、残念なことに現在の所、ブラウザが想定しているフォーマットと少しずれているのか、Safariではうまく動きませんでした。仕組みは簡単なので、原因が分かれば修正も簡単にできるでしょう。

「/ws/chat」は、チャットの入力となるHttpのURIです。g3のWebSocketチャネル「wschat」を経由して、WebSocketのポート「/ws/chat」にデータを出力しています。

WebSocketのポートとして、「/ws/chat」と「/ws/timer」が公開されています。「/ws/chat」は前述のように、HTMLのURIの「/ws/chat」からループバックして接続されています。このループバックによってチャット機能を実現しています。

「/ws/timer」は、タイマーからg3のWebSocketチャネル「wstimer」を経由して、タイマーからの入力を受取り、WebSocketに送信しています。

g3は、RESTのセマンティクスを軸にコンポーネントを疎結合して、非同期メッセージング、イベント駆動で動作させるフレームワークですが、WebSocketやServer-Sent Eventもシームレスに統合できることが確認できました。
上記のプログラムは、チャネル間を配線しただけの簡単なものですが、アプリケーションロジックをチャネルのエージェントとして配備することでより複雑な処理ができるようになります。
たとえば、一定時間ごとにEvernoteにアクセスして、取得した結果をWeb SocketやServer-Sent Eventでプッシュ配信する、というようなアプリケーションを簡単に作れるでしょう。

2010年12月15日水曜日

Androidのアーキテクチャ

このところAndroidに集中して取り組んでいるのですが、その作業の中で、Androidアプリのアーキテクチャとして使えそうなものが見えてきたのでメモしておきます。

図で赤くなっているオブジェクトは、Androidの基本クラスでAndroid OS上で特別な機能を担うものです。これらのオブジェクトをベースとして、どのようなオブジェクト群を追加し、どのような責務配分をして全体をバランスさせるのかという点が論点となります。

このアーキテクチャでは、ControllerとModelという2つのクラスを導入して、上記の配線でActivity、Handler、Thread、Serviceを結びつけています。
ここで注意が必要なのは、ControllerとModelという名前。他に良い名前がないのでControllerとModelという名前を付けていますが、気持ちとしては(MVCではなく)PACアーキテクチャパターンのControlとAbstractionを意図しています。
UIデバイス周りの低レベルイベントはActivity(PACのPresentation)で処理し、Controller(PACのC)はUX領域のセマンティックイベントの処理を行います。
一方Serviceの方は、利用者以外のアクター、すなわち外部サービスや時間といった外部事象からのイベント処理を行います。
Modelは、アプリケーションロジックを記述します。データベースやファイルそしてDriver経由で外部サービスの呼出しを行います。
外部との連携はDriverとReceiverの2つのオブジェクトを用います。Driverは従来的なPull型の連携を行うドライバです。Receiverはクラウド的なプッシュ型連携を受け取るドライバです。

Androidは、ちょっと本格的なアプリケーションになると内部的には非同期メッセージを駆使したプログラミングモデルになってくるので、そのあたりをどうさばくのかというがアーキテクチャを考える上での軸の一つとなります。
Real-Time UML的にはタスク設計的なことも必要になります。

この図では直接見えてきませんが、このアーキテクチャは、Androidのスレッド構造にも配慮しています。肝になるのが、GUI用スレッドがGUIリソースを専有している点。Handlerが鍵となるオブジェクトです。


また、クラウドとスマートデバイスを統合したトータルでのアプリケーションアーキテクチャとの中でも機能するアーキテクチャである点も重要です。
この点は、非同期メッセージをプログラミングモデルの基盤に据えることで要件を満たせると考えており、その点をアーキテクチャに盛り込んでいます。

2010年11月27日土曜日

アプリケーションアーキテクチャとデータベース

引き続き右のクラウドアプリケーションアーキテクチャについて考えています。

JJUG CCC 2010 Fallでは、CQRSアーキテクチャパターンの動きを下の図を用いて説明しました。
これを実現する場合、Commandによる更新系とQueryによる問合せ系で、物理的なデータベースを分けるのがよいわけですが、さらにデータベースのシステムも性質に合わせたものを適材適所で選択するとより良い結果が得られるはずです。

問合せ系のデータベースとしては、ここまでの議論からカラム指向データベース/HBaseが候補となっています。

Commandとして投入されたEventはコミットログ的な実現方式で、データベースに格納します。このデータをここではEventログと呼ぶことにします。Eventログを格納するデータベースは、通常オペレーションではappend-onlyなので、分散ISAM的なシンプルなデータベースが向いています。shared nothingなのでshardingでスケーラビリティを確保するのも容易です。このあたりの性質を軸にデータベースを選択することになります。append-only&shardingに強い、更新が高速で信頼性の高い分散KVSが候補となりますが、まだ見つけていないので保留。現時点ではRDBMSを(ISAM的に)使うのがよいかもしれません。

問題は、Eventログの内容をカラム指向データベースに反映するところです。
カラム指向データベースは、問合せに強い反面、更新に弱い性質があります。このため、カラム指向データベースを通常のトランザクション向けデータベースとして使うのは、あまり得策ではありません。

本アーキテクチャでは、このギャップをドメインサービスにおいて、トランザクションデータの格納にインメモリデータベースを使うことで埋めることが眼目の一つになっています。インメモリデータベースはVoltDBが候補になっています。
アプリケーション実行中のトランザクションは、インメモリデータベースに格納・管理されます。カラム指向データベースはあくまでもインメモリデータベースのバックアップなので、更新に多少の時間がかかっても問題ありません。
また、データの永続性はEventログのデータベースによって担保されるため、バックアップデータベースであるカラム指向データベースへのデータ更新は、インメモリデータベースへの更新とは非同期に、バックグラウンドでゆっくり行っても大丈夫というわけです。

以上のように、分散KVS(またはRDBMS)、インメモリデータベース、カラム指向データベースを適材適所で組合わせてアプリケーションを構築すると面白い結果が得られそうです。
まだまだ机上の議論ですが、この方向で考察を深めていきたいと考えています。

2010年11月26日金曜日

データベースの選択

カラム指向データベースにHBaseを使うと、Hadoopとの連携がスムーズになるので、バランスがよさそうというお話をしました。

こうなると、インメモリデータベースも具体的に考えてみたくなります。

当初は、SQLiteのインメモリデータベースモードを使って、自分でメモリクラスタを組むようなことを考えていたのですが、色々調べてみるとどうもVoltDBがよさそうに思えてきました。

VoltDBはRDBMSなので、SQLの豊富なデータ操作機能を用いることができます。
いわゆるKVSを使うと、アプリケーション側で相当の作り込みが必要なので、この点でRDBMSは安心感があります。

VoltDBは、自動的にメモリクラスタを組んでくれるみたいなので、この点でもアプリケーションは何もしなくて済みそうです。
メモリクラスタによってスケーラビリティを高めるとともに、信頼性を向上させることができます。

VoltDBではJavaを使ったストアドプロシジャとしてプログラムを書く必要があるので、この点がちょっとクセのあるところです。
たとえば、本アーキテクチャの場合、ドメインサービスだけでなく、アプリケーションサービスもストアドプロシジャとして実現するような実現方式も検討が必要になります。
この点に問題がなければ、かなり有力な選択肢ではないかと思います。

インメモリデータベースの弱点は、メモリクラスタが完全にダウンした場合の永続性の確保と、大規模データがメモリに載り切らないリスクです。
本アーキテクチャでは、Eventログを用いて永続性は確保できているので、メモリにデータが載り切る場合には、Eventログの分散KVS(または普通のRDBMS)とVoltDBだけでシステムが組むこともできそうです。(AWSの場合、メモリモデルとして7.5GB, 15GB, 34.2GB, 68.4GBが用意されており、一般的なアプリケーションではメモリのみで運用することが可能です。すでにそういう時代に入っています。)
ただし、メモリクラスタ再起動時にEventログから最新の状態を復元するのに時間がかかりそうなので、何らかのチェックポイントをどこかに保存して置く必要があります。
そうなると、結局のところバックアップデータベースとしてHBaseを併用するのがバランスがよさそうです。

図の左側にあるマスターデータはローディングが早ければ何でもよいので、HBaseに相乗りで十分でしょう。

以上の考察から、今の所:

  • Eventログ用DB:append-only&shardingで高速書き込みできる分散KVSの何か
  • ドメインサービスのインメモリデータベース:VoltDB
  • バックアップデータベース&Hadoop:HBase
  • マスターデータベース:HBase(に相乗り)
というのが面白そうと考えています。

2010年11月25日木曜日

Hadoopの置き場所

土曜日に書いたアーキテクチャは、Hadoop座談会で得られたインスピレーションによるものでしたが、肝心のHadoopの置き場所を考えるのを忘れていました。
そこで、考えてみたのが以下の図です。


アーキテクチャ的には、MapReduceやその実装としてのHadoopだけでなく、より汎用的な概念の導入が有効ではないかということでBackground Computingとしています。
さらに細かく考えていくと、Background ComputingもBatchとRealtime(Streaming)に分けることができそうですが、これはいずれ。

ボクは、クラウドアプリケーションとは、非同期に発生するイベントを連続して受け取りながら、イベント受信をうけて内部状態を刻一刻と遷移させていくオブジェクトと考えています。右の図はこのあたりをJJUG CCC 2010 Fallでお話した時に使ったものです。
この中で、インデクサとしているものが、上の図のBackground Computingに相当します。
クラウドアプリケーション自身はイベント駆動で動作しますが、その応答性能や結果精度を向上させるために、必要な情報をバックグラウンドで常に作り続けているのも、クラウドアプリケーションのもうひとつの側面になります。
この部分の実現機構として、MapReduce/Hadoopが重要な選択肢というわけですね。

Hadoopを組み合わせるということで具体的に考えてみると、HBaseはカラム指向データベースということに思い至りました。アーキテクチャ素案では、ドメインサービスのメインデータベースはインメモリデータベースで、このバックアップとしてカラム指向データベースを併用しています。このバックアップデータベースをHBaseとすることで、Hadoopとの連携をシームレスに行う事ができるようになります。

Background Computingの結果は、バックアップデータベースに直接戻すケースと、イベントとして通常のデータ更新ルートに載せるケースの両方が必要でしょう。
そのあたりを図に追加しています。

2010年11月20日土曜日

クラウドアプリケーションのアーキテクチャ素案

きのうはHadoop座談会(第3回)。佐藤先生と萩原さんのお話を聞くことができました。
並行・並列・分散技術。RDBMS JoinからDryad。旬のテーマで参考になる点が多々ありました。
これに加えて、懇親会で萩原さんからお聞きしたカラム指向データベースのお話が刺激的。クラウドアプリケーション・アーキテクチャのインスピレーションが浮かんだので、図にまとめてみました。


CQRS、Event Sourceのアーキテクチャの上で:

  • 各種データベースの選択とアーキテクチャ責務分割
  • プレゼンテーション、アプリケーション、ドメインの各サービスのアーキテクチャ責務分割
  • 可用性の担保
  • スケーラビリティの担保
といったものを織り込んでいます。
Domain Serviceのクラスタを構築してin memory databaseでワークデータの管理をするのが面白いかなと思っているのですが、Domain Service初期化時にすべてのデータをメモリにローディングするのは現実的ではないので、バックエンドのデータベースが必要となります。ここにカラム指向データベースがぴったりはまるのではないか、というのがインスピレーション。RDBMSでもよいところですが、スケーラビリティを高めるにはカラム指向データベースという選択が有効と考えられます。
エージェント,というとやや大げさですが、ストアドプロシジャとかクロージャとか、そういった技術でデータの近くで処理を行って結果を返す機能が必要になるのは明らかなので、そのあたりもアーキテクチャに取り込みたいところ。ここは、Domain Service界隈で実現できるのではないかと考えています。
他にも色々と話題があるので、この図をもとにブログで実現方式を整理していこうと思っています。