BT

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

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Atomサーバー ~ データ分散のためのパブリッシングの力 ~ パート2

Atomサーバー ~ データ分散のためのパブリッシングの力 ~ パート2

このシリーズのパート1(参考記事)で、私たちはAtomサーバー(リンク)を紹介しました。それは、Atomストアをつくるための拡張性のあるオープンソースフレームワークです。Atomストアは、GData(リンク)形式でAtomパブリッシングプロトコル(リンク)を結合することに基づいた、データサービスアーキテクチャにおける新しいトレンドです。

AtomサーバーはHomeaway.comのロード回数がとても多く、高い可用性のデータ流通システムにて用いられました。というのも、大きくそしてさまざまな種類のクライアントとの通信が必要で、Atomサーバーは特にクライアントを中心に進化してきたからです。これは、AtomPubの仕様にいくつかの拡張機能を付加することに動機づけられました。一番注目すべきものは、自動タグ、バッチ、そしてアグリゲートフィードです。

自動タグ

カテゴリは、AtomPubで最も有用なコンセプトの一つです。Atomカテゴリ(リンク)は、基本的に名前-値のペア(Atomでは、これらをとスキームとタームと呼んでいます。)であり、追加的なメタデータとしてAtomエントリに関連があります。これは、クライアントがオリジナルのデータを操作することなく、データを分類して新しい関係を必要に応じて適用することができる強力なコンセプトです。

Atomサーバーは、自動的にエントリのカテゴリメタデータを保持します。これらのカテゴリ(例えばANDやOR)に対して、しっかりとしたBooleanのクエリーのメカニズムも提供しています。(GDataも同様のメカニズムで、それはこのシリーズのパート1(参考記事)で詳細を説明しました。

Atomサーバーにおいてとても重要な役割を演じているカテゴリですが、私たちが初めに優先させた機能の一つは、「自動タグ」エントリの機能でした。自動タグ機能は、送信されたエントリ(PUT)のコンテンツをベースとし自動的にカテゴリを計算します。

自動タグ付けは、EntryAutoTaggerインターフェースを実装することによって達成されます。EntryAutoTaggersは、Spring(リンク)のようなIOCコンテナを使っているAtomサーバーのワークスペースに実装されます

最も一般的なエントリのコンテントタイプはXMLです。それゆえ、AtomサーバーではXPathAutoTaggerの実装を提供しています。この EntryAutoTaggerは、そのコンテントXMLに対してXPath表現を実行することをベースとしたエントリのためのカテゴリ生成が簡単にできるように構成されています。

例えば、以下のコンテントXMLを例にとります。

<widget id="123">
    <color>red</color>
    <size>small</size>
</widget>

/widget/colorや/widget/sizeのようなXPath表現は、(urn:color)redや(urn:size)smallのカテゴリを生成するでしょう。クライアントは、これらのカテゴリを明示的につくったり、それらの存在を認識する必要すらありません。それどころか、 XPathAutoTaggerはXMLコンテンツを読み込んでカテゴリを自動的に生成します。フィードリーダーは、カテゴリ化されたフィードを取得し、要求した全てのカテゴリが存在することを認識します。さらに、システムはカテゴリをきちんと管理するために、潜在的に信頼できないクライアントには依存しません。

アグリゲートフィード

Atom ストアは、通常、複数の異なるワークスペースや相互に関係のあるデータで構成されたコレクションを内包しています。例えば、ある企業が各従業員に対するエントリで一つのワークスペースを含むAtomストアで、別のワークスペースは従業員たちの会議を含むものをもっているとします。給与支払い部門は、給与手続きのために従業員のフィードを取得したいでしょうし、ビル管理人は会議予約を監視する必要があります。これらは共に、最も単純なAtomPubフィードの例です。

他の例として、すべての会議が各従業員と結合しているフィードを望んでいるプロジェクトマネージャーがいるとしましょう。マネージャに対し、データセットを関連づけている間に2つの別々のフィードの取得を強制するのではなく、従来のAtomPubでそうしているように、Atomサーバーではアグリゲートフィードの概念を加えています。アグリゲートフィードは、すべてのエントリ、別々のワークスペースやコレクションであっても、単一のデータフィードとして結合することができます。

Atomサーバーは、カテゴリの強力な概念を利用してアグリゲートフィードをつくります。アグリゲートフィードに対するスキームを結びつけるために、特定のスキーム(先に述べたカテゴリのスキーム/タームのペア)が選択されます。アグリゲートとして結び付けられなければならない2つ以上のエントリは、同じ結合スキーム内の同じタームでタグ付けられなければなりません。

アグリゲートフィードは、ワークスペースが$joinとして指定したURIを利用して特定されます。そして、コレクションは、同じ結合スキームとして特定されます(例. $join/{Join Scheme})。アグリゲートフィードURIは、そのURIで指定されたスキームに存在しているユニークなタームのそれぞれに対して、一つのエントリを含んでいます。これらのエントリは、アグリゲートの適切なコンポーネントエントリを含むアグリゲート要素として、そのエントリの内容にアグリゲートされます。

例に戻り、その企業にはAtomストア内に以下のデータがあると仮定します。({workspace}/{collection}/{entryId}といった形の、標準的なAtomサーバーのURI構造で設計されています)

/employees/acme/cberry.xml
    <employee name="Chris Berry" id="123" dept="dev"/>
/employees/acme/bjacob.xml 
    <employee name="Bryon Jacob" id="345" dept="dev"/>
/meetings/acme/standup.xml
    <meeting name="standup" time="Every Tuesday 9:15" >
       <employee id="123" />
       <employee id="345" />
         </meeting>

プロジェクトマネージャーが「従業員の会議」のアグリゲートフィードを引き出す前に、私たちは、結合したいそれぞれのエントリに関して適切なAtomカテゴリをつくらなければなりませんでした。それは、唯一のカテゴリのスキームが「アグリゲートカテゴリ」を定義していて、スキーム内にあるそれぞれのカテゴリのタームが「カテゴリエントリ」を定義しています。これは、ほとんどの場合、前述したAutoTaggerのメカニズムを利用するでしょう。

私たちがurn:EIDと自身の結合スキームを設定した場合、エントリに対して以下のカテゴリを定義するでしょう。

/employees/acme/cberry.xml <- (urn:EID)123
/employees/acme/bjacob.xml <- (urn:EID)345
/meetings/acme/standup.xml <- (urn:EID)123
                              	(urn:EID)345 

これらのカテゴリがエントリに適用されるとともに、プロジェクトマネージャーは、スキーム「urn:EID」をベースとしたアグリゲートフィードをすぐに引き出すことができます。すべてのアグリゲートフィードに対するワークスペースが$joinであるので、使用するURLは以下のようになります。

http://your.atomserver/$join/urn:EID

urn:EIDスキームのユニークなタームごとに一つのエントリでアグリゲートフィードを返します。

それぞれのエントリの内容は、要素です。それは、カテゴリにマッピングしている「真の」エントリそれぞれに対して EntryのXMLのセットを含んでいます。たとえば、アグリゲートフィードのレスポンスは、以下のようになるでしょう。簡潔さのために、XML要素の多くが削除されていることに注意してください。

 <feed xmlns="http://www.w3.org/2005/Atom"
          xmlns:as="http://atomserver.org/namespaces/1.0/">
    <as:endIndex>16573</as:endIndex>
    <id>tag:atomserver.org,2008:v1:urn:EID</id>
    <entry>
       <id>/atomserver/v1/$join/urn:EID/345.xml</id>
       <as:entryId>345</as:entryId>
       <content type="application/xml">
          <aggregate 
             xmlns="http://schemas.atomserver.org/atomserver/v1/rev0">
             <entry xmlns="http://www.w3.org/2005/Atom">
                <id>/atomserver/v1/employees/acme/bjacob.xml</id>
                <as:entryId>bjacob</as:entryId>
                <as:workspace>employees</as:workspace>
                <as:collection>acme</as:collection>
                <content type="application/xml">
                   <employee
                      xmlns="http://schemas.atomserver.org/examples" 
                      name="Bryon Jacob" id="345" dept="dev" />
                </content>
             </entry>
             <entry xmlns="http://www.w3.org/2005/Atom">
                <id>/atomserver/v1/meetings/acme/standup.xml</id>
                <as:entryId>standup</as:entryId>
                <as:workspace>meetings</as:workspace>
                <as:collection>acme</as:collection>
                <content type="application/xml">
                   <meeting 
                         xmlns="http://schemas.atomserver.org/examples"
                         name="standup" time="Every Tuesday 9:15">
                      <employee id="123" />
                      <employee id="345" />
                   </meeting>
                </content>
             </entry>
          </aggregate>
       </content>
    </entry>
    <entry>
       <id>/atomserver/v1/$join/urn:EID/123.xml</id>
       <as:entryId>123</as:entryId>
       <content type="application/xml">
          <aggregate
             xmlns="http://schemas.atomserver.org/atomserver/v1/rev0">
             <entry xmlns="http://www.w3.org/2005/Atom"
                <id>/atomserver/v1/employees/acme/cberry.xml</id>
                <as:entryId>cberry</as:entryId>
                <as:workspace>employees</as:workspace>
                <as:collection>acme</as:collection>
                <content type="application/xml">
                    <employee 
                       xmlns="http://schemas.atomserver.org/examples"
                       name="Chris Berry" id="123" dept="dev" />
                </content>
            </entry>
            <entry xmlns="http://www.w3.org/2005/Atom">
               <id>/atomserver/v1/meetings/acme/standup.xml</id>
               <as:entryId>standup</as:entryId>
               <as:workspace>meetings</as:workspace>
               <as:collection>acme</as:collection>
               <content type="application/xml">
                  <meeting 
                     xmlns="http://schemas.atomserver.org/examples" 
                     name="standup" time="Every Tuesday 9:15">
                     <employee id="123" />
                     <employee id="345" />
                  </meeting>
               </content>
            </entry>
         </aggregate>
      </content>
   </entry>
 </feed>

アグリゲートフィードに関して注意すべき重要事項がいくつかあります。

  • アグリゲートフィードに対するシーケンス番号(その番号はタグで返され、Atomサーバー内の一貫したフィードページングに利用されます)は、その「子のエントリ」すべての中で最も大きいシーケンス番号と同じです。重要なのは、アグリゲートの要素のいずれかが変わると、次にそのアグリゲートフィードがとるときに、そのアグリゲートが返されるということです。
  • アグリゲートエントリに対するカテゴリのセットは、すべてのコンポーネントにおけるカテゴリの結合です。
  • アグリゲートフィードはカテゴリ検索を前提とするかもしれません。したがって、リクエスト/$join/urn:EID/-/(urn:department)は、(urn:department)devのカテゴリで定義した私たちのフィードからすべてのアグリゲートエントリをとります。
  • ローカライズされたアグリゲートフィードは、通常のフィードと同様にして指定されます(例 /$join/urn:EID?locale=en_US)。少なくともコンポーネントの一つが所定の場所にローカライズされていれば、ローカライズされたアグリゲートエントリだけは存在します。ローカライズされたエントリがなければ、そのアグリゲートはフィード内で何も返されません。

アグリゲートフィードは、 要素の内部のエントリで新たな3つのXML要素を定義します。

  • <as:workspace>- コンポーネントエントリのワークスペースを含む
  • <as:collection>- コンポーネントエントリのコレクションを含む
  • <as:locale>- コンポーネントエントリのロケールを含む(もしあれば)

これらの要素によって、アグリゲートフィードのコンシューマがプログラムで容易にアグリゲートのそれぞれのコンポーネントの特性を決めることができます。

バッチ

それぞれのエントリに対して別々のサーバー呼び出しにしようとすると、潜在するラウンドトリップオーバーヘッドがあるため、オペレーションの集まりをバッチ化して単一のリクエスト(POST、PUT、DELETE)にすることでシステムのパフォーマンスメリットが得られます。残念ながら、AtomPubではバッチのメカニズムを提供していないので、私たちはAtomサーバーにバッチの機能を追加しました(GDataの同様の機能からヒントを得ました)。

おそらく、バッチ処理の実装をよりRESTfulにするのであれば、HTTPの中のマルチパートの機能を使うべきでしょう。しかし、特に私たちのクライアントが利用する言語で適用範囲が広いものを考慮し、この技術はクライアントにとって複雑すぎると判断しました。代わりに、私たちはURLベースの仕組みを選択しました。

私たちは、バッチが記述されていることを意味する「BATCH」といったカスタムのHTTPメソッドの利用も少しだけ考えました。しかし、再び、クライアントのインターオペラビリティの最大化を考え、私たちは「標準的な」HTTPメソッドのセットにこだわることに決めました。そして、バッチ操作を示すために別のURIを使用しました。

Atomサーバーでのバッチ操作は、$batchという特別なエントリIDを使用するコレクションの「仮想」エントリをPUTすることによって完了します。たとえば、以下のURLです。

PUT http://your-atom-server/widgets/acme/$batch

widgetsワークスペース内のacmeコレクションでのバッチ操作を示しています。このURIの構造は、与えられたバッチ操作が特定のワークスペースとコレクションの中のエントリだけに適用されるという点に注意してください。

バッチのPUTに対するリクエストのXMLの内容は、Atomフィードで、バッチ内のそれぞれのアイテムに対するAtomエントリを含んでいます。エントリは、いくつかの可能な拡張を加え、特定のリクエストとして正確につくられます。

Atomサーバーは、http://atomserver.org/namespaces/1.0/batchの名前空間の中に、新たな拡張XML要素を宣言します。それは、クライアントがフィードのそれぞれのエントリがupdate (PUT)なのか、insert (POST)なのか、あるいはdelete (DELETE)なのかを指定することができます。これらの要素は、グローバルにバッチフィード全体に適用されるか、それぞれのエントリ個々に適用されるかのいずれかです。

例えば、バッチリクエストは以下のようになります。

<feed xmlns="http://www.w3.org/2005/Atom"
 xmlns:asbatch="http://atomserver.org/namespaces/1.0/batch">
   <entry>
     <asbatch:operation type="update" />
     <link href="/widgets/acme/123.xml/*" rel="edit" />
     <content type="xhtml">
       <div xmlns="http://www.w3.org/1999/xhtml">
         <widget id="123>
           <color>red</color>
           <size>small</size>
         </widget>
       </div>
     </content>
   </entry>
   <entry>
     <asbatch:operation type="delete" />
     <link href="/widgets/acme/234.xml/*" rel="edit" />
   </entry>
 </feed>

エントリ123を更新し、234を削除しようとします。

バッチが解釈され、処理された後、バッチ内の個々のエントリがエラーであったとしても、バッチフィードのHTTPレスポンスは200 OKです。

XMLレスポンスの内容は、この場合も、初めのバッチリクエストのそれぞれのエントリに対応する一つのエントリのAtomフィードです。これらのエントリは、対応するバッチリクエストにおいて同じ順序で現れます。

これらのレスポンスエントリのそれぞれにおいて、Atomサーバーは、エントリが単独のオペレーションとしてサブミットされる場合に、返されるHTTPのステータスコードを意味するカスタム要素を追加します。成功したエントリに対し、レスポンスは次のようになります。

<asbatch:status code="200" reason="OK"/>

あるいは、

<asbatch:status code="201" reason="CREATED"/>

エラーが発生すると、エラーコードと理由が与えられます。たとえば、

<asbatch:status code="404" reason="NOT FOUND"/>

あるいは、

<asbatch:status code="409" reason="Optimistic Concurrency Error"/>

さらに、どのくらいのinsertsupdatesdeleteserrorsが発生したのかを示すためのフィードレベルとして要素があります。上記の例に対して、以下を受け取ります。

<asbatch:results inserts="0" updates="1" deletes="1" errors="0"/>

この「ロールアップ」レポートの検査は、クライアントがレポートされたエラーが0でないときに特定のエラーに対する備えを固めることができます。

さらに

RESTful な設計によってスケーラビリティやインターオペラビリティを可能とするので、私たちはAtomPubの上にAtomサーバーを築き上げました。標準による構築によって、私たちの問題に立ち向かうためにAtomコミュニティの多くの人々の知恵を活用することができます。インスピレーションのためにGData に目を向け、私たちはバッチや楽観的同時並行性といったいくつかの有用な拡張を追加しました。私たちがAtomサーバーの利用法を拡張するにつれ、自動タグやアグリゲートフィードを含む追加機能の必要性が明らかになりました。拡張性のあるAtomの性質のお陰で、恐れることなく、これらの機能を既存の Atomクライアントと共にインターオペラビリティを追加することが容易にできました。Atomの力は、私たちがこれらの強力な機能を、私たちのクライアントの多くとともに味わうこの単純なインタラクションを犠牲にすることなく追加することができたときに真に明らかなものとなりました。それは、両者のそれぞれの長所と言えます。

2008年5月のオープンソースのデビュー以来、Atomサーバーはかなりの関心を生み出しました。私たちはより多くの人々がAtomサーバーを選択し、それを利用し、私たちにそれの足りない点を教えてくれることを望んでいます!Atomサーバーは、http://www.atomserver.orgからダウンロードすることができ、数分で実行することができます。

 

原文はこちらです:http://www.infoq.com/articles/atomserver2
(このArticleは2008年9月26日に原文が掲載されました)

この記事に星をつける

おすすめ度
スタイル

BT