BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル 1杯のコーヒーを得る方法

1杯のコーヒーを得る方法

ブックマーク

私たちは、CORBA、Webサービスのプロトコルスタック、J2EEなどの実装といったミドルウェアプラットフォーム上の分散システムを構築するのに慣れています。本稿では、私たちは異なるアプローチをとり、アプリケーションプラットフォームとしてのWebをつくるためのプロトコルとドキュメントを扱います(それは、軽量なミドルウェアを通してアクセスされます)。私たちは、単純な顧客サービスインタラクションのケースをとおして、アプリケーション統合のシナリオにおけるWebの役割を説明します。さらに、私たちは、近刊書「GET /connected - Web-based integration」(仮題)の中のいくつかの考えを抽出し、それを共有するために、主要な設計哲学としてWebを使用します。

はじめに

既知のことですが、インテグレーションの領域は変化し続けています。Webの影響や、よりアジャイルなプラクティスへと向かっている傾向において、良いインテグレーションの構成に関する考えはより興味深いものとなっています。システム間でおこなわれた専門家の活動の代わりに、さらに悪いことに結果論ですが、現在、インテグレーションは成功のソリューションのありふれたものです。

しかし、Webの影響は、エンタープライズコンピューティングにおいて未だに大きく誤解され、過小評価されています。Webに精通した人たちでさえ、WebがHTTP上のXMLをサポートするミドルウェアソリューションでもなく、ざっくりとしたRPCのメカニズムでもないということを理解するのにしばしば苦労しています。Webは、単純なポイント・ツー・ポイント接続よりも多くの価値をもつものであるため、この事実は残念なことです。

本稿で、私たちはWebの興味深い利用法をいくつか紹介し、エンタープライズシステムで非常にクールなことをするための、柔軟で堅牢なプラットフォームとしてそれをあつかいたいと思います。さらに、ワークフローを超えるエンタープライズソフトウェアの典型となるものがないことについても....

何故、ワークフローなのか?

ワークフローはエンタープライズコンピューティングに欠かせないもので、実際、ミドルウェアによって絶えず実装されてきました(少なくとも、コンピュータの条件の中で)。ワークフローの構造は、ステップの変わり目を促すための多くの個々のステップやイベントに織り込まれます。ワークフローによって実装される包括的なビジネスプロセスは、しばしば、複数のエンタープライズ情報システムにわたり、ワークフローを統合作業のための肥沃な土壌とします。

スターバックス:標準的で一般的なコーヒーは標準的で一般的な統合にふさわしい

Webが、エンタープライズ(とさらに広い)統合のための有効な技術であるならば、それがワークフローをサポートできなければなりません。それは、いくつかのより大きなビジネスの可能性を実現するために、別々のシステムの間のインタラクションをきちんと強調させるものだからです。

現実世界のワークフローのを正しく扱うために、私たちが多くの技術的かつドメイン特化の詳細について扱う必要があることは疑いの余地がありません。それは、本稿の目的をあいまいなものとするかもしれないので、私たちは、Webベースの統合作業の方法を説明するためのより身近なドメインを選択しました。それは、Gregor Hohpe氏のスターバックスコーヒーショップのワークフローです。Gregor氏の有名なブログの投稿の中で(リンク)、収益を得るための切り離されたパイプラインとしてスターバックスがどのように機能しているかについて説明しています。

スターバックスは、他のビジネスと同様に、注文のスループットを最大化することを第一に考えています。注文が多ければ多いほど儲かるからです。その結果、スターバックスは非同期処理をします。お客が注文をすると、レジ係はコーヒーカップに注文の印を付けて、キューに送ります。キューというのは、エスプレッソマシン上に一列に並んでいる、文字通りコーヒーカップのキューです。このキューによりレジ係とバリスタが切り離され、バリスタが少し遅れたとしてもレジ係は注文を受け続けることができます。これにより、店が忙しくなったら、Competing Consumer のシナリオのように複数のバリスタを配置することができます。

Gregor氏は、メッセージ指向ミドルウェアのEAI技術としてスターバックスのモデルを選んだので、私たちも同じシナリオをモデルとし、Web リソース(統一インターフェースをサポートするアドレス可能なエンティティ)を扱います。実際、私たちは、従来のEAIツールと関連するすべての信頼性とともに、Web技術がどのように利用されるのかを説明しようと思います。そして、なぜリクエスト/レスポンスプロトコル上のXMLメッセージングよりも、 Webの方がはるかに良いかについても説明します。

ここでの私たちの目的は、スターバックスを完全かつ正確にモデル化することでなく、Webベースのサービスでワークフローを説明することなので、私たちがスターバックスの作業方法を勝手に扱うことに対し、事前に謝罪します。前置きが長くなりましたが、さっそく始めましょう。

分かりきっていること

私たちはワークフローについて話題にしているので、ワークフローの構成やある状態から状態へとワークフローが遷移するイベントを理解する必要があります。私たちの例には2つのワークフローがあり、それらをステートマシンとしてモデル化しました。これらのワークフローは並列に実行されます。一つ目のモデルは、図1にある客とスターバックスのインタラクションで、もう一つが、図2のバリスタによって実行されるアクションのセットをとらえたものです。

客のワークフローでは、客はスターバックスのサービスとのインタラクションによって、コーヒーを飲むというゴールに向かって進みます。ワークフローの一部として、客が注文して支払を行い、飲み物が来るのを待つということを前提としています。注文から支払までの間に、客はしばしば注文を変更します。たとえば、低脂肪ミルクを求めたりします。

図1 顧客のステートマシン

客には見えませんが、バリスタは自身のステートマシンを持っています。それは、人目につかないサービスです。図2に見られるように、次の注文を探し、飲み物を準備し、支払を受けとるという行為を繰り返します。その繰り返しのインスタンスは、注文がバリスタのキューに追加されたときに始まります。そのワークフローのアウトプットは、バリスタが注文を完了し、飲み物を渡したときに、客が利用できるものです。

図2 バリスタのステートマシン

これらのすべては、Webベースの統合とは100万マイル離れたもののように感じるかもしれませんが、2つのステートマシンのそれぞれの遷移は、 Webリソースでのインタラクションを表しています。それぞれの遷移は、状態変化を引き起こしているURIを経たリソースにおけるHTTPメソッドの組み合わせとなります。

状態遷移を引き起こさないので、GETとHEADは特別です。代わりに、リソースの現在の状態を調べることができます。

しかし、私たちは少し先走りすぎています。ステートマシンやWebについていっぺんに考えることは、簡単なことではありません。ですから、初めからシナリオ全体に戻り、Webコンテキストでそれを考え、一歩づつ進んでいきましょう。

顧客の視点

私たちは、まず初めに、プロセス全体の始まりとなる単純なストーリーカードで始めようと思います。:

このストーリーには、いくつかの有益なアクターとエンティティが含まれています。初めに、カスタマーがいます。彼は、(内在する)スターバックスサービスにおいて明らかなコンシューマです。次に、2つの興味深いエンティティ(コーヒーと注文)があります。そして、そのインタラクション(注文)によって、ワークフローが始まります。

スターバックスに注文を送信するために、私たちは、単純に、よく知られたスターバックスの注文のURIに、注文の表現をPOSTします。それは、http://starbucks.example.org/orderとなります。

図3 コーヒーを注文する

図3は、スターバックスで注文をするためのインタラクションを示しています。スターバックスは、そのドメインからエンティティを表現するために、 XMLを使用します。興味深いことに、XMLは情報も埋め込むことができるので、客は注文プロセスを進めることができます(間もなく見ることになるでしょう)。POSTの内容は、図4のようになります。

ヒューマンWebおいて、コンシューマとサービスは表現形式にHTMLを使用します。HTMLは、それ自身に特定のセマンティクスを持っていて、すべてのブラウザで解釈され採用されています。たとえば、 <a/>は「別のドキュメントや、同じドキュメント内のブックマークへのリンクのためのアンカー」を示しています。コンシューマアプリケーションであるWebブラウザは、単純にHTMLをレンダリングし、ステートマシン(それはあなたです!)は、GETやPOSTを利用してリンクをたどります。Webベースの統合でも同様で、サービスやコンシューマがインタラクションのためのプロトコルに合意するだけでなく、その表現の形式やセマンティクスについても合意しなければなりません。

図4  飲み物の注文をPOSTする

スターバックスサービスは注文のリソースを作成します。そして、HTTPヘッダのLocationの中に新しいリソースのロケーションを設定し、コンシューマに応答します。便宜上、サービスもまた、レスポンス内に新しく作られた注文リソースの表現を置きます。そのレスポンスは、このようになります。

図5  注文が行われ、支払を待つ

201 Created ステータスは、スターバックスが首尾よく注文を受け付けたことを示します。Location ヘッダには、新しく作成された注文のURIが設定されます。レスポンス部の表現には、料金と共に、何が注文されたのかの確認が含まれています。加えて、この表現にはリソースのURIが含まれており、それは、コンシューマのワークフロー処理を進めるための、スターバックスが私たちに期待するインタラクションです。

そのURIが、<a/>タグではなく、<next/>タグに含まれていることに注意してください。ここで、<next/>は、客のワークフロー(演繹的に、どちらが同意されるかというセマンティクス)のコンテキストとして意味のあるものです。

私たちは、201Createdステータスコードがリソースの作成に成功したことを示すということを既に見ました。私たちは、今回の例や一般的なWebベースの統合の両方で、他のいくつかの有益なコードも必要とします。:
200 OK - これは、私たちが期待するものです。全てがOKなので、先に進みましょう。201 Created - 私たちは、ちょうどリソースを作ったところです。すべて問題ありません。
202 Accepted - サービスが私たちのリクエストを受け付け、レスポンスのロケーションヘッダにURIを設定し、私たちを誘導します。
303 See Other - 別のリソースとのインタラクションが必要です。おそらく、OKです。
400 Bad Request - リクエストを再フォーマットし、それを再送する必要があります。
404 Not Found - そのサービスがあまりに遅延している(あるいは、セキュアである)ため、私たちのリクエストが失敗した本当の理由を知ることができません。しかし、理由はどうあれ、私たちはそれに対処しなければなりません。
409 Conflict - 私たちは、リソースの状態を更新しようと試みました。しかし、サービスはそれに対しうまく対処しません。私たちは、リソースの現在の状態を得る(あるいは、レスポンスのエンティティ部を確認したり、GETをする)ことで、どこへ行くべきかを知ることができます。
412 Precondition Failed - リクエストは、Etagにより処理されませんでした。If-Match、あるいはそれに類するガードヘッダの条件に合致しませんでした。私たちは、次の処理を行う方法を知る必要があります。
417 Expectation Failed - あなたは、確認で正しい処理を行いました。しかし、実際にリクエストを送信しようとしないでください。
500 Internal Server Error - T究極の遅延レスポンス。サーバーは正しく処理されず、理由も不明です。成功を祈ります...

注文を更新する

スターバックスに関する素晴らしいことの一つに、無限に異なる方法で飲み物のカスタマイズができることです。実際、より進んだ客の中には、科学式での注文の方がよいだろうと思うくらいの、自身が求めるアップグレード数を持っています。しかし、ここではそういった野心的なことはやめましょう(少なくとも、そこから始めることはありません)。代わりに、私たちは別のストーリーカードを見たいと思います。

図4を振り返ると、私たちは、明らかに重要な過ちを犯しています。それは、コーヒーが本当に好きな人に対して、エスプレッソのシングルショットがたっぷりのホットミルクとなるでしょう。私たちは、それを変える必要があります。幸いにも、Web(あるいは、より正確なHTTP)はそういった変更のサポートを提供しており、そこで、私たちのサービスを行います。

初めに、私たちが今もなお自身の注文を変更することができるということを確認しておきます。時折、私たちが注文を変更しようとする前に、バリスタが即座にコーヒーを作り、香り豊かなホットミルクコーヒーを受け取ることがあります。しかし、バリスタがすこしもたもたしているときは、バリスタがコーヒーを作る前に、私たちに注文を変更するチャンスが与えられます。私たちが注文を変更できるかどうかを見極めるためには、図6にあるように、HTTPのOPTIONSメソッドを利用して何のオペレーションをする準備をしているのかについてリソースに問い合わせます。

リクエスト レスポンス
OPTIONS /order/1234 HTTP 1.1 Host: starbucks.example.org 200 OK Allow: GET, PUT

図6 OPTIONSで問い合わせる

図6から、リソースが理解しやすく(GETをサポートし)、更新しやすい(PUTをサポートしている)ものであることがわかるでしょう。私たちは、Webの世界における善良な市民として、新しい表現のPUTを試すのに、実際のPUTを行う前にExpectヘッダを使用していつでも水のテストを行うことができます。図7のようになります。

リクエスト レスポンス
PUT /order/1234 HTTP 1.1 Host: starbucks.example.com Expect: 100-Continue 100 Continue

図7 慎重に!

注文の変更が既にできなければ、図7の「慎重な」リクエストに対するレスポンスは、417 Expectation Failedとなるでしょう。しかし、ここでのレスポンスは100 Continueなので、図8で示されるように、私たちはエスプレッソの追加ショットでリソースを更新するためのPUTを行なうことができます。更新されたリソース表現を有効にPUTすることで、既存のそれを変更します。この場合、PUTにより重要なエキストラショットを含む<additions/>要素で新しい説明を与えます。

部分的な更新は、RESTコミュニティ内での深い哲学的な議論の対象ですが、今回、私たちは実用的なアプローチをとり、追加ショットのリクエストが既存のリソース状態のコンテキスト内で処理されるものと仮定します。それ故、それぞれのオペレーションに必要なネットワークを通したリソース表現全体を動かす意味はほとんどありません。つまり、私たちは差分のみを送信します。

図8 リソースの状態を更新する

私たちが新しいリソースの状態を更新するためのPUTを首尾よく実行した場合、図9で示すように、私たちはサーバから200のレスポンスを得ます。

図9 リソースの状態を首尾よく更新する

OPTIONSを確認し、Expectヘッダを使用することで、その後のリクエストの失敗を引き起こすサービスを変えるような状況のすべてを保護することができるわけではありません。そのようなものとして、私たちがそれらの使用を命令しているのではなく、とにかく、私たちは405409のレスポンスをWeb世界の良き住人として扱いたいと考えています。

OPTIONS、特にExpectヘッダを使用するのは、オプションの方法であることを考慮してください。

私たちが、ExpectOPTIONSを賢明に使用していたとしても、たまにPUTが失敗するでしょう。結局、私たちはバリスタと競争しています。そして時折、彼らはうまくこなします。

私たちがエキストラショットを獲得するためのレースに負けた場合、私たちがリソースの更新のためにPUTをしようとするときにそれについて知るでしょう。図10のレスポンスは、私たちが予測することのできるものによく見られるものです。409 Conflict は、そのリソースが更新するのに矛盾した状態であることを示しています。そのレスポンス部は、私たちがPUTしようとした表現とサーバーサイドのリソースの状態の違いを表しています。コーヒーの場合だと、ショットを追加するのがあまりにも遅すぎる状態です。つまり、バリスタは既にホットミルクを注いでいます。

図10 競争に負ける

私たちは、できる限り競合の状態を避けるために、ExpectOPTIONSを使用することについて議論してきました。これらに加え、受信しているサービスに私たちの意図を伝えるために、PUTIf-Unmodified-SinceIf-Matchヘッダをつけることもできます。If-Unmodified-Sinceはタイムスタンプを使用し、If-Matchはオリジナルの命令のETag1 を使用します。私たちがその命令を作成してから変更されていなければ(つまり、バリスタがまだ私たちのコーヒーの準備を開始していなければ)、その変更が処理されるでしょう。注文が変わっていれば、412 Precondition Failed のレスポンスを受け取るでしょう。競争に負けた場合は、私たちはミルクコーヒーを受け取りますが、少なくともそのリソースが矛盾した状態になることはありません。

Webの一貫した状態の更新に対するパターンは、いくつか存在します。HTTP PUTはべき等で、状態の更新における複雑な作業の多くを引き受けます。しかし、そのために必要となる選択がまだ存在します。以下は、正しく更新をおこなうための私たちのレシピです。

 

1. OPTIONSを送信することで、PUTがまだ可能かどうかをサービスに問い合わせてください。この方法はオプションです。問い合わせの際に、サーバがリソースに対しどの動詞をサポートするかについての手がかりをクライアントに与えます。しかし、そのサービスが永久に同じ動詞をサポートするという保証はありません。

2. サーバーに対し不要なPUTの実行を防ぐための手助けとして、If-Unmodified-SinceIf-Matchヘッダを使用します。その後でPUTが失敗したら、412 Precondition Failedを受け取ることになるでしょう。このアプローチは、If-Unmodified-Sinceのためにゆっくりとリソースを変更する(1秒の精度)か、If-MatchのためにETagをサポートするのかのどちらかに依存します。

3. 更新のPUTに対する全ての409 Conflict responseへの対処を早急におこなってください。私たちが(1)と(2)を利用するとしても、私たちのガードとチェックはもともと楽観的なものなので、これらのレスポンスに対処しなければならないでしょう。

W3Cには、矛盾した更新の検知・対処に関する参考資料(リンク)があり、そこではETagの利用を主張しています。ETagは、私たちが推奨するアプローチです。

私たちのコーヒーの注文を更新するという大変な作業の後、私たちがエキストラショットを得ることだけが正しいことのように思われます。したがって、とりあえずは私たちの幸せの道へ進むこととし、私たちがエスプレッソの追加ショットが得られたと仮定します。もちろん、私たちが支払を済ませなければ、スターバックスはコーヒーを手渡してくれないでしょう(それについては、既にそれとなく言っていたのはお分かりでしょう!)。では、次の話に移ります。

私たちの最初の注文に対するレスポンスに、要素があったことを覚えているでしょうか? これは、注文の表現の別のリソースに関して、スターバックスが埋め込んだものです。私たちは、そのタグを既に見ましたが、私たちの注文が正しかったのでそれを無視しました。しかし、今回はそれをより詳しく見てみます。

<next/>要素には、注目に値するポイントがいくつかあります。初めに、状態の変化がスターバックスに限定されていないので、それが異なる名前空間にあるということです。この場合、私たちは、そのような遷移のURIは再利用性(または、最終的な標準化)を促進するために共通の名前空間であるべきだと判断しました。

そして、rel属性の中に組み込まれた意味情報(言わば、プライベートなマイクロフォーマット)があります。http://starbucks.example.org/paymentという文字列のセマンティクスを理解しているコンシューマは、ワークフローの次の状態(支払)に遷移するためのuri属性によって識別されたリソースを使用することができます。

<next/>要素のuriは、支払のリソースを指しています。type属性から、私たちは期待されたリソース表現がXMLであることを予め知っています。私たちは、OPTIONSを使用して支払リソースがどの動詞をサポートするのかをサーバーに問い合わせることで、そのリソースをどうすべきかについて知ることができます。

マイクロフォーマットは、既存のドキュメントに構造化され、より意味のあるデータを埋め込むための手法です。マイクロフォーマットは人間が読むことのできるWebの中で最も共通的なもので、カレンダーのイベントのような構造化された情報表現をWebページに追加するために利用されます。また一方で、それらは統合の目的としても利用できます。マイクロフォーマットの用語は、マイクロフォーマットコミュニティによって承認されていますが、ドメイン特化のセマンティックマークアップのために、私たちが独自のプライベートなマイクロフォーマットを作るのは自由です。

図10の一つのような単純なリンクは、RESTコミュニティがやや冗長に言っている「アプリケーション状態のエンジンとしてのハイパーメディア」(より単純に言えば、URIがステートマシンの遷移を表現する)に関する最も重要なポイントです。クライアントは、本稿の初めで見たように、後に続くリンクによってアプリケーションのステートマシンを操作します。

このモデルについて最も驚くべきことの一つは、このモデルが、あなたがステートマシンとワークフローをナビゲートすることで、それらが徐々に自身を記述する手法であるということです(よく知られているWS-BPELやWS-CDLでの記述と比べ)。しかし、あなたの脳を180度回転することを止めると、リソースのための次へのリンクによって、アプリケーションの様々な状態に遷移できるということが理解できるでしょう。それぞれの状態遷移で、現在のリソース表現が次に可能なリソースやそれらが表現する状態へのリンクのセットを含んでいます。そして、次のリソースが単にWebリソースなので、私たちはそれらをどうすれば良いのかを既に知っています。

顧客のワークフローの次のステップは、コーヒーの支払いをすることです。私たちは注文の要素から合計金額を知ります。しかし、私たちがスターバックスに支払をおこなう前に、図11にあるように、私たちがどのように情報をやりとりするのかを支払いリソースに問い合わせます。

コンシューマは、サービスに関する事前情報をどれくらい必要とするのでしょうか? 私たちは、サービスやコンシューマがインタラクションの前にお互いにやりとりする表現のセマンティクスに合意する必要があるということを既に推奨してきました。それらの表現形式を可能な状態と遷移のセットとして考えてください。コンシューマがサービスと相互作用するにつれ、サービスは利用可能なセットから状態と遷移を選択し、次の表現を構築します。そのプロセス(即ち、ゴールに到達するための「方法」)は、作業しながら見いだされます。しかしながら、そのプロセスの一部として共に結ばれたものは、事前に合意されたものです。

 

コンシューマは、一般的に、設計と開発の中でサービスによる表現と遷移のセマンティクスに合意します。とは言え、サービスが進化するにつれ、クライアントが全く予期していなかった状態の表現や遷移に直面する可能性がでてきます。しかし、それに対してどう処理するかを知っています。それが、疎結合なWebの性質です。しかしながら、これらの状況下でリソースの形式や表現に合意することは、本稿の範囲外です。

私たちの次のステップは、コーヒーの支払いをすることです。私たちは、注文の表現の中に埋め込まれた要素から、私たちの注文の合計額を知ります。そこで、私たちの次のステップとしてスターバックスへの支払いをすれば、バリスタは飲み物を渡してくれるでしょう。初めに、図11で示すように、私たちがどのように支払リソースとのインタラクションをおこなう方法についてそれに問い合わせます。

リクエスト レスポンス
OPTIONS/payment/order/1234 HTTP 1.1 Host: starbucks.example.com Allow: GET, PUT

図11 支払方法を知る

そのレスポンスは、私たちが(GETを通して)支払を参照するか、(PUTを通して)それを更新するかのいずれかが可能であることを示しています。価格を知ることで、私たちは先に進み、paymentのリンクで指定されたリソースに私たちの支払いをPUTします。もちろん、支払は機密情報なので、認証2を要求することでリソースへのアクセスを保護します。

リクエスト

PUT /payment/order/1234 HTTP 1.1
Host: starbucks.example.com
Content-Type: application/xml
Content-Length: ...
Authorization: Digest username="Jane Doe"
realm="starbucks.example.org“
nonce="..."
uri="payment/order/1234"
qop=auth
nc=00000001
cnonce="..."
reponse="..."
opaque="..."


<payment xmlns="http://starbucks.example.org/">
   <cardNo>123456789</cardNo>
   <expires>07/07</expires>
   <name>John Citizen</name>
   <amount>4.00</amount>
</payment>

レスポンス
Response 201 Created
Location: https://starbucks.example.com/payment/order/1234
Content-Type: application/xml
Content-Length: ...

<payment xmlns="http://starbucks.example.org/">
   <cardNo>123456789</cardNo>
   <expires>07/07</expires>
   <name>John Citizen</name>
   <amount>4.00</amount>
</payment>

図12 請求書を支払う

支払の成功に対し、図12で示される交換が必要です。認証されたPUT201 Createdのレスポンスを返せば、私たちは支払が成功したことに満足し、私たちの飲み物を得ることができます。

しかし、間違った方向に進み、金額がおかしな状態になったときは、彼らがおこなったことに対し、間違った方向へ進まないかリカバリ可能な状態が望まれます3。たとえば、明らかに、以下のことで支払がうまくいきません。

  • サーバーダウン、または未到達により、サーバーと通信ができない;
  • サーバーへの接続が、インタラクションの間のあるポイントで切断された;
  • サーバーが4xxまたは5xxのエラーステータスを返した

有難いことに、Webはこれらのシナリオで私たちを助けてくれます。初めの2つのケース(接続の問題が一時的なものであると仮定します)では、私たちが成功のレスポンスを受け取るまでの間、再度、支払をPUTするだけです。私たちは、先のPUTが実際に成功した場合(事実上、サーバーから操作不能の認識)は200のレスポンスを、新しいPUTが最終的に支払に成功した場合は201のレスポンスを期待します。同様のことが、サーバが500503504のレスポンスコードを返す第3のケースで有効です。

4xxの範囲のステータスコードは、より扱いにくいです。しかし、それらは先に進む方法を示しています。たとえば、400のレスポンスは、私たちがサーバーにPUT したものが理解されず、それを再度PUTする前にペイロードを修正する必要があることを示しています。反対に、403のレスポンスは、サーバーが私たちのリクエストを理解したが、それを処理するのが拒否され、リトライも望んでいないことを示しています。そのような場合、私たちは代替の処理をおこなうためにレスポンスペイロードの別の状態遷移(リンク)を探さなければならないでしょう。

私たちは、サービスとの次のインタラクションをクライアントに指示するために、このサンプルでステータスコードを何度か使用しました。ステータスコードは、豊富なセマンティクスを持っています。意味のあるステータスコードとそれらの扱い方を知っているクライアントを持つサーバーを実装することで、私たちはHTTPの単純なリクエスト/レスポンスメカニズムの上で協調するプロトコルを階層化することができます。そして、分散処理のための高度な堅牢性と信頼性を加えることができます。

私たちが飲み物の支払いを済ませば、ワークフローの最後に到達し、コンシューマの物語の最後に進みます。しかし、それは物語全体の終わりではありません。サービスの内部に進み、スターバックスの内部の処理を見ることにしましょう。

バリスタの視点

顧客として、私たちは自分自身をコーヒー世界の中心に置きがちです。しかし、私たちはコーヒーサービスのコンシューマにすぎません。私たちはすでにバリスタと私たちの「競争」を知っていて、そのサービスは少なくとも一つの利害関係をもった組によって提供されるので、その最少単位はバリスタではありません。付加的なデリバリスタイルに合わせて、別のストーリーカードを用意します。

飲み物のリストは、Webフォーマットとプロトコルを使用して簡単にモデル化されます。Atomフィードは、未処理のコーヒー注文を含むすべてのリストとして非常によいフォーマットであるので、ここではそれを採用します。バリスタはフィードURIの単純なGETでAtomフィードにアクセスすることができます。それは、図13の未処理の注文であるhttp://starbucks.example.org/ordersです。

図13 作られる飲み物のためのAtomフィード

スターバックスは忙しいところなので、/orders のAtomフィードは頻繁に更新されます。したがって、バリスタは最新の状態を獲得する必要があります。ポーリングをすることが簡単なスケーラビリティを提供する一般的な考えです。しかし、Webは非常にスケーラブルなポーリングメカニズムをサポートしていることがすぐに分かるでしょう。スターバックスでは、毎分、膨大な量のコーヒーがつくられるので、負荷にあったスケーリングは重要な問題です。

ここで、私たちは2つの矛盾した要求に直面します。私たちはバリスタに注文フィードを頻繁にポーリングすることで最新にして欲しいと思っています。しかし、私たちは、サービスの負荷の増加や不必要なネットワークトラフィックを望んではいません。図14で示すように、負荷を受けることでサービスがクラッシュすることを避けるために、私たちはキャッシュのためにサービスの外側にリバースプロキシを使用して、リソース表現への頻繁なアクセスに対応します。

図14 スケーラビリティのためのキャッシュ

多くのリソースに対して(特に、広くアクセスされ、飲み物のためのAtomフィードのようなもの)、ホストサービスの外側でそれらをキャッシュしていることが分かります。これにより、サーバーの負荷を低減し、スケーラビリティを改善します。私たちのアーキテクチャにWebキャッシュ(リバースプロキシ)を追加することで、メタデータのキャッシュも含めて、クライアントがもとのサーバーへの負荷をかけることなく、リソースを取得することができます。

キャッシングのプラス面は、サーバーの一時的な障害を隠し、陽副作用は、それがサーバーの断続的な故障を覆い隠して、資源州の入手可能性を改善することによって回復シナリオを粉砕するのを助けるということです。つまり、順序情報が代理によってたくわえられた時からたとえスターバックスサービスが断続的に失敗するとしても、エスプレッソ職人は働き続けることができます。そして、エスプレッソ職人が命令(事故)を忘れるならば、命令が非常に利用できるので、回復はより簡単にされます。

もちろん、キャッシングは必要であるより長く古い命令をまわりで保つことができます。そして、それはスターバックスなどの高いスループット小売業者にとってほとんど理想的でありません。たくわえられた命令がすっきりすることを確認するために、スターバックスサービスは、反応が長くたくわえられることができる方法を宣言するために、Expiresヘッダを使います。消費者とサービスの間であらゆるキャッシュ()その指令を受賞して、そして、その代わりにスターバックスサービスの上に要請を進めて、新鮮でないorders4に供給することを拒否します、最新の順序情報があります。

飲物が将来に10秒停滞しているようになるように、図13の中の反応はExpiresヘッダを我々のAtom飼料の上に置きます。このキャッシングふるまいのため、キャッシュ基盤で残りを取り扱って、サーバーは分当たりの多くても6つの要請を予想することができます。比較的十分に実行していないサービスのためにさえ、分当たりの6つの要請は、対処可能な作業負担です。最も幸運な事例(スターバックスの見解から)では、エスプレッソ職人の投票要請はローカルキャッシュから答えられます。そして、さらなるネットワーク活動またはサーバーロードに終わりません。

我々の例に、我々はスケール我々のマスターコーヒーリストの同性愛を暴露するのを助けるために、1つのキャッシュだけを使います。本当のウェブに基づくシナリオは、しかし、いくつかの層のキャッシングから利益を得るかもしれません。既存のウェブキャッシュを利用することは、高いボリューム状況でスケーラビリティのために重要です。

ウェブは、大量のスケーラビリティと待ち時間を交換します。あなたが待ち時間(例えば外国為替取引)に非常に敏感である問題領域を持つならば、ウェブに拠点を置く解決は素晴らしいアイディアでありません。しかし、あなたが秒または分さえまたは時間のオーダーで待ち時間を受け入れることができるならば、ウェブはたぶん適当なプラットホームです。

我々がスケーラビリティを提出した今、より機能的な懸念に戻りましょう。エスプレッソ職人が我々のコーヒーを準備し始めるとき、更なる最新版が許されないように、命令の国は変わらなければなりません。顧客の観点から、これは我々が我々の命令(図6、図7、図8、図9と図10の場合のように)のPUT最新版にもはや入ることができない瞬間と一致します。

幸いにも、我々がこの仕事のために使うことができるはっきりしたプロトコルが、あります:Atom Publishingプロトコル(別名APPまたはAtomPub)。AtomPubは、Atom飼料のエントリを管理するためのウェブ中心の(URIに基づく)プロトコルです。我々のコーヒーを中で意味している入場のより詳細な観察をしましょう?/、命令Atomは、供給します。

図15 コーヒー注文のためのAtomエントリ

図15のXMLには、いくつかの興味深いことがあります。初めに、フィード内のその他すべての注文から私たちの注文を見分けるためのAtom XMLがあることです。そして、注文それ自身があり、バリスタが私たちのコーヒーを作るのに必要なすべての情報(それにはとても重要なエキストラショットが含まれています)が含まれています。注文エントリの中には、それを変更するためのURIが記述されたlink要素があります。変更のためのURIは、HTTPを通して編集可能な注文リソースへのリンクです。(このケースでの変更可能なリソースのアドレスは、たまたま注文リソースそれ自身と同じアドレスになっていますが、そうである必要性はありません)

私たちの注文がもはや変えられることのないようにバリスタがリソースの変更状態を必要とし、彼らはその変更のためのURIを通して注文のインタラクションをとります。具体的には、図16に示すようにして、変更のためのURIに対してリソースの状態の変更バージョンをPUTします。

図16 AtomPubを経由した注文状態の変更

サーバーが図16のPUTリクエストを処理すると、/orders/1234リソースへのGETリクエスト以外はすべて拒否するでしょう。

注文が決まったところで、バリスタは安心してコーヒーをつくることができます。もちろん、バリスタは私たちにコーヒーを渡す前に、私たちが注文に対する支払をしたかを知る必要があるので、コーヒーを手渡す前に、バリスタは私たちが支払をおこなったことを確認します。実際のスターバックスでは、事情が少し異なります。たとえば、注文の支払などの会話があり、他の客はあなたが彼らの飲み物を持って立ち去ることのないようにうろうろしています。しかし、私たちのコンピュータ版では、この確認を追加するのは、さほど大変な作業ではありません。そこで、最後のひとつ前のストーリーカードを見てみます。

バリスタは、注文の中にある支払URIを使用して支払リソースをGETすることで、支払状況の確認を簡単におこなうことができます。

この場合、顧客とバリスタは、注文表現の中に組み込まれたリンクから支払リソースを知ります。しかし、時々、URIテンプレートによるリソースのアクセスが役に立ちます。

URIテンプレートは、よく知られたURIのための記述フォーマットです。テンプレートによって、コンシューマが異なる資源にアクセスするためにURIの一部を変えることができます。

URIテンプレートのスキームは、AmazonのS3ストレージサービスをサポートしています。格納されたアーティファクトは、テンプレートからつくられたURI(http://s3.amazonaws.com/{bucket_name}/{key_name})によって、HTTP動詞を使用して操作されます。

私たちのモデルで支払と同様のスキーマを推測するのは容易なので、バリスタ(あるいは、別に認可されたスターバックスシステム)は他の支払を説明されなくても対応する支払に素早くアクセスすることができます。そのスキーマは、以下のとおりです。: http://starbucks.example.org/payment/order/{order_id}

URIテンプレートは、コンシューマとの契約を結びます。それ故、サービスプロバイダはサービスの進化とともにその契約の維持に注意する必要があります。暗黙の結合により、一部のWebインテグレーターはURIテンプレートを敬遠します。私たちのアドバイスは、推測できるURIが有効で、変更が発生しないようにそれらを利用することです。

私たちの例における他のアプローチは、それぞれの支払いリソースに対し(推測できない)リンクを含んだ/paymentsフィードを公開することでしょう。そのフィードは、認証されたシステムにのみ利用可能となります。

最後に、URIテンプレートがハイパーメディアを通じた安全で有効な近道であるのは、サービス設計者次第です。私たちのアドバイスは、それらを慎重に利用してください!ということです

もちろん、誰もが支払を参照できるわけではありません。私たちは、より創造的(でしっかりとしていない)コーヒーコミュニティのメンバーにそれぞれのクレジットカードの詳細をチェックさせたくないので、しっかりとしたWebシステムで認証を要求することで、私たちは慎重を期するリソースを保護します。

認証されていないユーザーやシステムが特定の支払いの詳細を参照しようとすると、図17に示すように、サーバーは彼らに対して証明書を要求します。

リクエスト レスポンス
GET /payment/order/1234 HTTP 1.1 Host: starbucks.example.org 401 Unauthorized WWW-Authenticate: Digest realm="starbucks.example.org", qop="auth", nonce="ab656...", opaque="b6a9..."

図17 支払リソースへの許可されていないアクセスがおこなわれた

401ステータス(有効な認証のメタデータ)は、私たちに再度リクエストをしなければならないことを教えてくれます。しかし、このときに適切な証明書を求めます。適切な証明書(図18)でリトライすると、私たちは支払を取得し、注文http://starbucks.example.org/total/order/1234の合計を示したリソースと比較します。

リクエスト レスポンス
GET /payment/order/1234 HTTP 1.1 Host: starbucks.example.org Authorization: Digest username="barista joe" realm="starbucks.example.org“ nonce="..." uri="payment/order/1234" qop=auth nc=00000001 cnonce="..." reponse="..." opaque="..." 200 OK
Content-Type: application/xml
Content-Length: ...
<payment xmlns="http://starbucks.example.org/">
   <cardNo>123456789</cardNo>
   <expires>07/07</expires>
   <name>John Citizen</name>
   <amount>4.00</amount>
</payment>

図18 支払リソースへの許可されたアクセス

バリスタの準備が整い、コーヒーが出され、支払を済ませると、彼らは未払いの飲み物リストから完了した注文を削除しようとします。いつものように、これをストーリーとしてとらえます。

私たちの注文フィードのそれぞれのエントリが、自身のURIを編集可能なリソースと識別するので、個々のリソースそれぞれにHTTP動詞を適用することができます。バリスタは、図19のように、単純にリストから削除するために関連するエントリによって参照したリソースをDELETEします。

リクエスト レスポンス
DELETE /order/1234 HTTP 1.1 Host: starbucks.example.org 200 OK

図19 完了した注文を削除する

フィードから項目を削除すると、フィードに対する新たなGETは、DELETEされたリソースを持たない表現を返します。私たちがきちんと動作するキャッシュを持ち、キャッシュ期限のメタデータをきちんとセットしていれば、注文エントリをGETすることで、404 Not Found レスポンスを直接返します。

あなたは、Atomパブリッシングプロトコルがスターバックスのドメインで私たちのニーズのほとんどを満たしていることに気づいたかもしれません。直接、コンシューマに/ordersフィードを公開した場合、顧客はフィードに飲み物の注文をパブリッシュするためにAtomPubを利用することができ、時間とともにそれらを変更することもできます。

発展:Webでの生活に関する事実

私たちのコーヒーショップは自己記述のステートマシンをベースとしているので、ビジネスニーズの変更のためにワークフローを変更することはとても容易です。たとえば、スターバックスがコーヒーの提供を始めた後すぐに、無料のインターネットプロモーションを提供することを選択するかもしれません。:

  • 7月 ? 新しいスターバックスショップは、本稿を通して見てきたように状態遷移と表現で標準的なワークフローを提供しています。コンシューマは、これらのフォーマットと表現を念頭にサービスとのインタラクトをおこないます。
  • 8月 ? スターバックスは、無料のワイヤレスプロモーションのために新しい表現を導入します。コーヒーのワークフローは、提供の状態遷移を提供しているリンクを含んで更新されます。URIの魔法のおかげで、そのリンクは内部のスターバックスリソースと同じくらい簡単にサードパーティのパートナーとなります。

    その表現はもともとの遷移を含んでいるので、既存のコンシューマもゴールに到達することができます。とはいうものの、しっかりとプログラミングしたものではないので、プロモーションのメリットが得られないかもしれません。

     
  • 9月 ? コンシューマアプリケーションとサービスがアップグレードされたので、彼らは無料のインターネットプロモーションを知り、それを利用することができます。そして、それらが発生するときはプロモーションの遷移に従うように指示されます。

うまく発展するためのキーは、常に変更を予期したサービスのコンシューマにあります。リソースに直接バインドする代わりに(たとえばURIテンプレートを通して)、それぞれのステップで、サービスはコンシューマがインタラクト可能な指定されたリソースへのURIを提供します。これらの指定されたリソースの一部は、解釈されず無視されます。その他はコンシューマが望んでいる既知の状態遷移を提供します。いずれの場合でも、このスキームはコンシューマとの互換性を保ちながらすばらしい発展を可能とします。

あなたが楽しもうとしている技術はとてもホットです

コーヒーを手渡すことで、私たちをワークフローの終了に導きます。私たちは注文し、その注文を変更(あるいは変更はできません)し、支払を済ませて最終的にコーヒーを受け取ります。カウンターの向こう側で、スターバックスは支払の受取と注文の管理で一様に忙しいです。

私たちは、ここで、Webを利用して必要なインタラクションすべてをモデル化することができました。Webによって、新しい例外や失敗を考える必要なしに、いくつかの単純で不幸なルート(たとえば、注文処理中に変更ができない、あるいは、すでにできあがっている)のモデル化を可能にしました。 HTTPは私たちが必要とするものすべてをすぐに提供しました。そして、不幸なパスでさえ、クライアントはゴールへ向って進むことができました。

HTTPが提供する機能は、最初は無害かもしれません。しかし、このプロトコルに関する世界的な合意と展開が既に存在します。そして、考えられるすべてのソフトウェアエージェントやハードウェアデバイスはそれをある程度理解します。私たちがその他の分散コンピューティング技術(例えばWS-*)のバルカン化された採用を検討するとき、私たちはHTTPがシステムとシステムの統合から解放される可能性を持ち、それを享受して、著しい成功を実現します。

Webは、非機能のソリューションをも支援しました。私たちが遷移に失敗したときは、GETPUTDELETEのようなべき等のふるまいをする動詞であるという共通認識により、安心してリトライすることができます。きちんとしたキャッシングにより障害を隠し、(強化された可用性で)クラッシュの回復を助けます。そして、HTTPsとHTTPの認証により、私たちの基本的なセキュリティニーズの手助けとなります。

私たちの問題領域はいささか人工的でしたが、私たちが強調したテクニックは、伝統的な分散コンピューティングのシナリオで適用可能なものです。私たちは、Webが単純であると偽るつもりはなく(あなたが天才であれば別です)、それが万能薬であると偽るつもりもありません(あなたがとても楽天家であるかREST信仰があれば別です)。しかし、Webは、ローカル、エンタープライズ、インターネットの規模での統合システムとして、堅牢なフレームワークであるということは事実です。

謝辞

著者は、Webでの「conversation descriptions」についてCardiff大学のAndrew Harrison氏に感謝します。

著者について

Jim Webber博士は、ThoughtWorksのプロフェッショナルサービスリーダーです。そこで彼は世界中のクライアントのために信頼できる分散システムアーキテクチャに取り込んでいます。Jim博士は、以前はイギリスのE-Science programmeでシニアリサーチャーでした。そこでは、Webサービスの実践としてグリッドコンピューティング連携の戦略や、信頼できるサービス指向コンピューティングのアーキテクチャパターンを開発し、広範なWebやWebサービスアーキテクチャの知識、そして開発経験があります。ヒューレッドパッカードでアーキテクトの後、Arjuna Technologiesに移り、Jim博士は業界第一のWebサービストランザクションのソリューションに関する開発リーダーです。Jim博士は、積極的なスピーカーであり、定期的に世界中のカンファレンスに招待されます。彼は、Webベースの統合に関する新しい本を執筆中で、「Developing Enterprise Web Services - An Architect's Guide」をはじめとして、積極的に執筆をおこなっています。Jim博士は、コンピュータサイエンスの理学士、並列コンピューティングで博士号を、共にニューカッスルアポンタイム大学で取得しています。彼のブログはhttp://jim.webber.nameにあります。

Savas Parastatidis氏は、ソフトウェアの哲学者でシステムとソフトウェアについて考えています。彼は、eResearchでテクノロジーの利用について研究しています。特に、クラウドコンピューティング、知識表現、マネジメント、ソーシャルネットワークに関心があります。彼は、現在マイクロソフトリサーチの外部研究チームにいます。Savas氏は、http://savas.parastatidis.nameでブログを楽しんでいます。

Ian Robinson氏(リンク)は、クライアントが初めから終わりまでビジネスとITが連携した、保守可能なサービス指向の機能を作ることを支援しています。彼は、マイクロソフトの技術でサービス指向のシステムを実装することに関して、指針を書いています。また、コンシューマ駆動のサービス契約とソフトウェア開発のライフサイクルのおける彼らの役割についての記事も書いています。ごく最近では、The ThoughtWorks Anthology (リンク)(Pragmatic Programmers, 2008)やInfoQでの記事(参考記事)があります。彼は、RESTfulなエンタープライズ開発やサービス指向でのテスト駆動の基盤を含むテーマについて、カンファレンスで定期的に公演しています。


1 ETag(Entityタグの省略形)は、リソースの状態に対する一意の識別子です。リソースのためのETagは、一般に、リソースデータのMD5のチェックサムかSHA1ハッシュです。

2 スターバックスの視点からどのように認証が機能するのかについては、後ほど見ます。

3 安全性が危機に瀕しているとき、第一に間違って遠くにいかないようにします!しかし、コーヒーを受け取ることが安全で重要なタスクではありません。

4 HTTP 1.1は、max-age、max-stale、max-freshを含むいくつかの有用なリクエストディレクティブを提供します。それは、キャッシュからの受け入れ準備ができ、クライアントが腐敗のレベルを知らせることができます。

 

原文はこちらです:http://www.infoq.com/articles/webber-rest-workflow
(このArticleは2008年10月2日に原文が掲載されました)

この記事に星をつける

おすすめ度
スタイル

BT