レベル: 中級 佐々木 庸平 (ysasaki2@jp.ibm.com), IT スペシャリスト, 日本アイ・ビー・エム システムズ・エンジニアリング株式会社 テクノロジーイノベーション 小出 理史 (mkoide@jp.ibm.com), IT スペシャリスト, 日本アイ・ビー・エム システムズ・エンジニアリング株式会社 テクノロジーイノベーション
2008年 10月 17日 本連載では今Web開発者が注目するApache CouchDB[1]に関する技術情報を紹介します。今回は5回シリーズの第3回として、CouchDB のビュー/クエリ機能であるMapReduceフレームワークの利用方法を学びます。
MapReduce とは
大量のデータ処理はコンピュータの得意種目です。ハードウェアなどが驚異的に進化した現在では、特別な注意を払っていないプログラムでも、相当量のデータを高速に処理できるようになりました。
しかし一方で、例えばネットワークの発展により、コンピュータに届くデータも爆発的に増加しています。その結果、大量のデータ処理に関する工夫が必要な場合も、依然として存在します。
ネットワークを経由して届けられる大量のデータを日々高速に処理している代表的な存在は、Googleでしょう。そのGoogle社員2人によって2004年に発表されたMapReduce[2]は、現在のハードウェア環境を生かした「工夫」の一例と見ることができます。発表後、多くの注目を集め、Apache Hadoopなどの実装[3]も登場しています。今回は、このMapReduceに関する概要説明と、CouchDBでの実装などについて説明します。そして、実際にデータベースのクエリプログラムを試して、その使い方を習得します。
MapReduceについての論文[2]の要旨冒頭部分には、「MapReduceは、大きなデータセットを処理/生成するためのプログラミングモデルであり、実装系である("MapReduce is a programming model and an associated implementation for processing and generating large data sets.")」と書かれています。似た特徴を持つ例を探してみますと、例えば選挙の開票作業があります。これは、「大きなデータセットを、多数の人が手分けして、できる限り短時間で処理する」ノウハウの代表と考えて良いでしょう。入力データの形式として投票用紙を用いることも、ノウハウの一部です。大変乱暴に書いてしまうと、
MapReduceのプログラミングモデル :選挙の開票作業手順(の骨格)
MapReduceの実装系 :投票用紙+投票箱+テーブル+計数機+ホワイトボード
をイメージして頂ければ、半分くらいは良い印象です。
- 投票用紙(記入済み)は、例えば選挙の種類ごとに違う色の用紙に、候補者名が書かれています。
- MapReduceの言葉なら、色がKey/候補者名がValueとなるデータが用意されていることに相当します。
- 通常、記入済み投票用紙は、投票箱に分散して保存されます。
- MapReduceでは、(入力)データの分散保存を強く考慮しています(データの近くで、処理を行います)。
- 開票作業は、各投票箱に近い場所に住んでいる作業員が、多数で一斉に行うものとします。
- 各投票箱に入っている投票用紙を全部テーブルに広げ、候補者名毎に(色を確認しながら)投票用紙を仕分ける作業は、"Map"の一例です。
- 各作業員は、(Key, Value)=(候補者名, 1)とするデータに、担当分の投票用紙を変換していることになります
。
- 集計は、全ての作業員の分類結果を候補者名毎に集め、計数機でカウントし、結果をホワイトボードに書き出すものとします。
まとめると、MapReduceを使うポイントとして、以下三点が挙げられます。
- (投票用紙相当の)データモデル(Key/Valueペア)を考案し、実行時(前)に、Key/Valueペアの値がセットされたデータセットを準備します
- (開票作業としてあらかじめ定義された)Map関数を呼び出します
- Map関数は1 で準備したKey/Value ペアを別のKey/Value ペアに変換する部分を担当します
- (集計作業としてあらかじめ定義された)Reduce関数を呼び出します
- Reduce関数はMap関数の後に呼び出され、(2) で生成されたKey/ValueのKeyでグループ化されたValueのリストを処理する部分を担当します
一般に、(2)のMapフェーズは並列実行(手分けして作業)が容易です。(3)のReduceフェーズでも、Key毎にグループ化した単位で独立して実行できるケースは少なくないでしょう。選挙の開票作業シミュレーションプログラムに限らず、この枠組みで目的のアプリケーションを書くことが出来れば、大量のデータに対する処理を効率よく行える要素を一つ手にしたことになります。以下では、MapReduceがCouchDBにどのように取り込まれているのか、詳しく見ていきます。
CouchDBにおけるMapReduceに基づくクエリ
問い合わせの流れ
前回の記事では、CouchDBがRESTに基づくCRUDオペレーションを提供することを説明しましたが、Readオペレーションは、ドキュメントのID(_idプロパティ)に基づいて1ドキュメントのみを読み出すオペレーションでした。CouchDBにおいて、複数のドキュメントに対する読み出しを行うためにはMapReduceに基づくクエリを記述する必要があります。CouchDBのクライアントから見えるクエリ実行の流れは次の通りです(図1)。
STEP1. Map関数とReduce関数をJSON形式のテキストドキュメントで記述する。
STEP2. HTTP PUTを使用してSTEP1で用意したドキュメントをデータベース上に登録する。
STEP3. STEP2の結果作成されたクエリ用のURIに対してHTTP GETでアクセスする。
STEP4. STEP2で登録したドキュメントに基づいてMapReduceが実行される (CouchDB上の処理)。
STEP5. クライアントに結果がHTTP Responseとして返される。
図1. CouchDB におけるクエリの流れ

1. で作成するJSONがクエリそのものです。CouchDBではデザインドキュメントと呼んでおり、次のような形式をしています。1つのデザインドキュメントには複数のクエリ処理を記述することができ、クエリ処理の単位(map, reduceの組み合わせ)をビューと呼びます。
{
"language" : "{programming_language}"
"views" : {
"{view_name}" : {
"map" : "function(doc){ ... }",
"reduce" : "function(key, values, rereduce){ ... }"
},
"{view_name}" : {
...
}
}
|
{programming_language} はmap関数およびreduce関数を記述するプログラミング言語名を記述します。CouchDBがデフォルトでサポートする言語はjavascriptで、これを指定するとオープンソースのJavaScript処理系であるSpider Monkeyで記述した関数が処理されるようになります。CouchDBの構成ファイルで設定を行えば別の言語、例えばRubyやLispなどを使用することも可能です(このクエリ処理専用の処理系を含むプロセスをQueryServerあるいはViewServerと呼びます)[4]。
{view_name}はビューの名前です。ビューの名前がURI(ビューURI)として公開されます。そして、1つのビューURIに対して、それぞれmap, reduce 関数を1つずつ定義します。尚、(文法上の問題ですが) JavaScript はfunctionを予約語として使用できますが、JSONではfunctionを使用できないことに注意してください。このため、デザインドキュメント上は文字列で関数を定義します(ダブルクオートで囲っています)。
クエリを使用可能にするには、このようにして作成したJSON形式のデザインドキュメントをHTTP PUTで登録します。登録用のURIは、http://{host}:{port}/{dbname}/_design/{design_name} です。登録が成功すると201 Createdを返します。尚、PUT処理によって_revプロパティがアサインされるので、既に登録済みのデザインドキュメントの更新を行う場合には、_rev 値を設定してHTTP PUTを発行する必要があります。http://{host}:{port}/{dbname}/_design/{design_name} に対してHTTP GETをリクエストすれば、デザインドキュメントの(_revを含む)完全なJSON形式のテキストを取得できるでしょう。
問い合わせ結果を取得するには、http://{host}:{port}/{dbname}/_view/{design_name}/{view_name} に対してHTTP GETでアクセスします。尚、このURIにはいくつかの追加パラメーターをURIのクエリパラメーターとして指定して、フィルタやソート、グループ化などの操作が行え、処理の結果は、JSON形式のテキストで取得できます。
mapとreduce
実例に入る前に、デザインドキュメント上のmapとreduce関数で書くべきロジックを理解するために動作の概要についてふれます。図と併せて確認してください。
まず、map 関数は引数を1つとるように定義します。この引数は、DB上にある1つ1つのドキュメントそのものが渡されます。CouchDBはデータベース上のすべてのドキュメントそれぞれをmapの引数として渡して呼び出します。つまりN個のドキュメントがデータベース上にあれば、N回のmapが独立して呼ばれます。オリジナルのMapReduceにおけるMapフェーズの入力はKey/Valueのペアですが、CouchDBにおけるMapフェーズの入力は、JSON形式で表現できるドキュメントです。CouchDBのドキュメントは、すべて _id という一意キーをもつため、CouchDBにおける入力もKey/Value のペアであるといってもいいでしょう。
map 関数の内部ではCouchDBが用意したemit(key, value)というCouchDBが用意している関数を使用して、Key/Value のペアを生成します。emit関数はmap関数内で何度でも呼び出すことができ、1つのドキュメントから複数のKey/Valueペアを生成することができます。また、emit関数の第1引数および第2引数のいずれも、任意の値(nullでもかまいません)を与えることができます。
ここまでを図に表すと図2のようになります。
図2. map フェーズ

emitによって生成されたKey/Valueペアは、CouchDBのデータベースディレクトリ上に、ビューファイルとして保存されます。尚、このとき、Keyの実体は emit(k, v) で渡された第1引数の値とmap関数を実行したドキュメントの_id値の2要素の配列となっています。Value には、emit(k,v)の第2引数の値がそのまま入っています。Key の内部にオリジナルのドキュメントの_id値を保持しているため、2度目以降mapを実行する必要がある場合には、オリジナルのドキュメントで変更があったもののみにmap関数を再適用して、ビューファイルを更新するようになっています。
次に、クライアントからView URIに渡されたクエリパラメーターを使用して、map関数の適用結果から、reduce関数に渡す第1引数(Keyの配列)と、第2引数(Valueの配列)を準備します。例えば、図3 は、何もクエリをしていしないとき、図4は、key=K1 でフィルタリングしたとき、図5 は group=true を渡してキー毎にグループ化したとき、となります。冒頭の説明で、「ReduceはKey/ValueのKeyでグループ化されたValueのリストを処理する部分を担当」という説明をしましたが、CouchDBにおいては、グループ化するかどうかはオプションです。尚、このフェーズで、グループ化を指定した場合は、グループの数だけReduceに渡すKey配列とValue配列の組み合わせ(図4の場合4つ)ができます。
図3. クエリパラメーターが指定されない場合

図4. keyパラメーターを指定してフィルタリングした場合

図5. groupパラメーターを指定してkey毎にグループ化した場合

View URI のクエリパラメーターを使用したKeyおよびValueの配列の準備が終わった後、reduce 関数が定義されていれば用意された配列をreduce関数に渡します。reduce関数が定義されていなければ、用意した配列をそのままクライアントに返します。
reduce関数は引数を3つとるように定義します。第1引数はKey配列、第二引数はValue配列です。第3引数は、rereduce と呼ばれるフラグを示すboolean値が格納されています。CouchDBは、1回のreduceの呼び出しでreduce処理を完了できない場合は、分割してreduce処理を実行するようになります。分割された個々のreduce処理は、第3引数がfalseで実行され、それらの結果を集めるときに再度reduce関数が呼ばれるときは、第1引数がnull, 第2引数が個々のReduce処理の結果のリスト,第3引数がtrueにセットされて呼び出されます。CouchDBでは2回目以降のreduce呼び出しをrereduceと呼んでいます。 Apache Hadoop などではCombineと呼ばれる処理に相当します。
図3. のKey配列、Value配列に対して、reduce を実行する場合の様子を示したのが図6です。rereduce が発生する場合に注意する必要があります。1回目のreduceはK1, K2 のKeyをもつリストと、K3, K4 のキーをもつリストの2つに分割されて実行されます。そして、各reduceの結果をマージするために再度reduceが走ります。reduce 関数は1つしか定義しませんが、第3引数のフラグによって、第1および第2引数の内容に差異があることを考慮してプログラムを記述する必要があります。
それでは実際に、デザインドキュメントを書いて試してみましょう。
図6. reduce の実行とrereduce の実行

Wiki アプリケーションにViewを追加する
ここでは、実際のアプリケーションでの利用例を確認するために、前回作成したWikiアプリケーションに対して、MapReduceを使ったクエリの機能をいくつか追加する方法を検討します。ただし、アプリケーションとして完成させるにはRailsのコードを修正する必要がありますが、本質はJavaScriptで記述するMapReduceの考え方および定義のやり方であるため、実際のRailsのソースコードには手を入れず、HTTPで問い合わせが可能になるまでを本項で解説します。
Wikiページにタギングを追加する
前回のWikiアプリケーションでページの構造は次のようになっていました。
{
"_id" : "titleに設定した値",
"_rev" : "CouchDBが設定するRevision番号",
"title" : "titleに設定した値",
"content" : "contentに設定した値"
}
|
タギングの構造として”tags”というメンバーを追加しましょう。タグは文字列の配列で追加すればよいでしょう。例えばruby, rails, couchdb というタグをつけたページは次のような構造になります。リレーショナルデータベースに慣れ親しんだ方であれば、正規化して多対多のテーブルを持ちたくなるかもしれませんが、CouchDBでは木構造のデータも格納できるので正規化は必ずしも正解であるとは限りません。
{
"title" : "Apache CouchDB と Ruby on Rails を使って wiki アプリケーションを作成する",
"content" : "ここはコンテンツです。" ,
"tags" : ["ruby", "rails", "couchdb"]
}
|
このような構造をしたデータがデータベース上に格納されていることを前提に、MapReduceによるクエリを考えてみます。次の二つがタグの用途としてあるでしょうか。
- 指定したタグにマッチするページのリストを取得する
- タグ毎の使用回数を取得する
尚、今回はサンプルデータを含めて必要なデータを付録のcouchiki.zip に添付しました。サンプルデータは、zipを展開したcouchiki/db/sample ディレクトリに格納されています。データの内容は、developerWorks Japan のフィードからtitle, content プロパティを取り出し、また、コンテンツの属するカテゴリ(複数のカテゴリに属すコンテンツがあります)をタグと見なしてデータを機械的に生成したもので、次のようなデータが入っています(content プロパティは使わないので省略しています)。また、すべてのデータに”developerWorks Japan”というタグを設定しています。
| title | tags |
|---|
| “Web時代の非リレーショナル….” | [“developerWorks Japan”, “Web development”, “Open source”] | | “XMLの構文解析での…” | [“developerWorks Japan”, “Architecture”, “XML”, “Java Technology”] | | “Amazon Web Service を …” | [“developerWorks Japan”, “Architecture”, “SOA and Web Services”,] | | … | … |
指定したタグにマッチするページの抽出
CouchDBのMapReduceフレームワークは、前述したとおりの実装になっており、一部のReduce操作をクエリパラメーターに基づいた処理テンプレートとして組み込んでいます。指定したタグにマッチするページ、と言う意味ではView URIにリクエストする際にkeyパラメーターを使用すれば抽出操作はできるでしょう。従って次のような流れでMapReduceを組み立てます。
Mapフェーズでは、WikiPageドキュメントに付与されたすべてのタグについて、(タグ, ページタイトル) のKey/Valueペアを作成することにします。例えば、本連載は、Web development およびOpen source のカテゴリで執筆していますので、意図的につけた developerWorks Japan というタグも含めて、3 つのKey/Valueのペアが作られるようにします。
大本のデータ:
| title | tags |
|---|
| “Web時代の非リレーショナル….” | [“developerWorks Japan”, “Web development”, “Open source”] |
Mapフェーズで生成されるKey/Valueペア:
| Key | Value |
|---|
| “developerWorks Japan” | “Web時代の非リレーショナル….” | | “Web development” | “Web時代の非リレーショナル….” | | “Open source” | “Web時代の非リレーショナル….” |
Mapフェーズの処理により、すべてのドキュメントからこのような (key, value) = (タグ, タイトル)というペアができれば、key=”Web development” といった指定で、同じタグを持つページを発見することができるでしょう。このケースでは、Reduceは実装する必要がありません。従って、デザインドキュメントは次のようになります。見やすさのため、map 関数の文字列内で改行をしていますが、実際には改行を含めずに記述します(couchiki/db/sample/tags.json に実際のJSONのファイルを用意しました)。
{
"_id" : "_design/tags",
"language" : "javascript",
"views" : {
"all" : {
"map" : "function(doc){
for(var i in doc.tags){
emit(doc.tags[i], doc.title);
}
}"
}
}
}
|
これを、適当なテキストファイルに保存してcurl –x PUTでCouchDBに登録します。
サンプルデータを使って実行結果を確認するには以下の手順を実施し、クエリおよびデータの登録を行います。尚、サンプルデータの送信には _bulk_docs という複数ドキュメント更新用APIを使用しています。
$ unzip couchiki_b.zip
$ cd couchiki
$ curl -X POST --data @db/sample/pages.json \
http://localhost:5984/couchiki_development/_bulk_docs
$ curl -X PUT --data @db/ sample/tags.json \
http://localhost:5984/couchiki_development/_design/tags
|
登録がうまくいったら、curl –X GET で参照して正常に動作することを確認してください。サンプルのデータであれば次のような形で結果が取得できるでしょう。日本語がUnicodeエンコードされ非常に読みにくいですが(しかしこれはJSONで日本語を使う限り仕方のないことです)、”key”プロパティに注目してください)。
$ curl -X GET http://localhost:5984/couchiki_development/_view/tags/all
{"total_rows" : 310 "offset":0, “rows": [
{"id" : "Amazon Web Services .." "key" : “Architecture", "value": ….}.
{"id" : "BRM …", "key" : "Architecture", "value": ….}.
{"id" : "EJB…", "key" : "Architecture", "value": ….}.
{"id" : "ESB …" "key" : "Architecture", "value": ….}.
{"id" : "IBM WebSphere…", “key": “Architecture", "value": ….}.
…
]}
|
ビューのレスポンスのJSONは、上記のように、{total_rows、offset、rows} のそれぞれの値を含めて返してくれます。rowsにmap関数の結果であるKey/Valueペアが含まれており、オリジナルのドキュメントのIDも必ず含まれます。total_rows は_design/tags/all のMap関数で生成されたKey/Valueのペアの総数を表します。タグ毎に生成すると全部で310件のタイトルがありますが、実際には1つのドキュメントに複数のタグが登録されているので、ドキュメント総数はもっと少ないでしょう。
では、この中から、XMLのタグがついたタイトルだけを取り出します。keyが文字列値XMLにマッチする、という条件をクエリに加えます。クエリに加えるときは、JSONのデータ型を意識してください。文字列を表現するためにダブルクオート(“)をURLエンコードした%22で囲います。
$ curl -X GET http://localhost:5984/couchiki_development/_view/tags/all?key=%22XML%22
{ "total_rows": 310 "offset":297, "rows" : [
{"id":"Ajax .." “key": "XML", "value" : ….}.
….
}
|
フィルタ処理を行いkey=”XML”のものだけを抽出しました。mapが生成するKey/Valueペアの数は310と変わりません(total_rows)が、”XML” にマッチするものはその297番目から開始される、ということがoffsetの値から読み取れます。
これで、データベースに対するクエリができるようになったので、あとはrubyのプログラムから、HTTP GETを使用してアクセスするだけです(この方法は前回説明しました)。
タグ毎の使用回数を取得する
次に、ブログパーツなどにあるようなタグクラウドを表現するために、タグの回数を数えることを考えてみます。リレーショナルデータベースではCOUNT関数などを使用して数を数えることができますが、CouchDBではMapReduceのReduceフェーズでカウントをJavaScriptで実装します。
先ほど見た通り、サンプルの中には、次のようなデータが存在します
| title | tags |
|---|
| “Web時代の非リレーショナル….” | [“developerWorks Japan”, “Web development”, “Open source”] | | “XMLの構文解析での…” | [“developerWorks Japan”, “Architecture”, “XML”, “Java Technology”] | | “Amazon Web Service を …” | [“developerWorks Japan”, “Architecture”, “SOA and Web Services”,] |
この表の中には、developerWorks Japan というタグが3回、Architecture というタグが2回、それ以外のタグが1回ずつ存在しています。これを計算するには次のようにします。
Mapフェーズでは、ドキュメントに付与されたすべてのタグについて、(タグ,1) というKey/Valueペアを作成します。つまり、上記の例では次のようなKey/Valueペアが生成されるはずです。
| Key | Value |
|---|
| “developerWorks Japan” | 1 | | “Web development” | 1 | | “Open source” | 1 | | “developerWorks Japan” | 1 | | “Architecture” | 1 | | “XML” | 1 | | “Java Technology” | 1 | | “developerWorks Japan” | 1 | | “Architecture” | 1 | | “SOA and Web Services” | 1 |
そしてReduceフェーズではValueの値を単純に足していけば、望みの結果が得られるはずです。key毎にグループ化するかしないか、はURIクエリパラメーターで与えるので、reduce関数内では気にする必要はありません。次のようなJSON形式のデザインドキュメントを用意します。
{
"_id" : "_design/calc",
"language" : "javascript",
"views" : {
"count_tag" : {
"map" : "function(doc){
for(var i in doc.tags){
emit(doc.tags[i],1);
}" ,
}",
"reduce" : "function(keys, values, rr){
return sum(values);
}"
}
}
}
|
sum はCouchDBのクエリ用に用意されている数少ないプリミティブ関数で、配列の合計を計算できます。つまり、Reduceフェーズでは 1+1+1+….+1 を計算しています(コラムも参照してください)。このデザインドキュメントを登録するために、やはりHTTP PUTを発行します。
$ curl -X PUT --data @db/sample/calc.json \
http://localhost:5984/couchiki_development/_design/calc.json
|
 | |
MapReduce のReduceフェーズでは、計算の順番に注意してください。例えば、Mapフェーズで[1,1,1,….] という大量の1 の配列が生成された場合、もしかしたら次のようにlengthプロパティを使用して配列の長さから合計を取得したいと思うかもしれません。
function(keys, values, rr){ return values.length; }
|
reduce が1回で必ず終了するならば、これは正しくどうさします。しかし、2回以上、つまりrereduceが発生するばあいは、このロジックでは正しく動きません。rereduceを考慮して以下のように書けば問題なく動きますが、しかし可読性が下がります。
function(keys, values, rr){ return rr ? sum(values) : values.length; }
|
reduce処理の1回目が[1,1,1,…] となっているならば、values.length と sum(values)は等価です。したがって、この場合は、rereduceが起こる場合も、起こらない場合もうまく動作するように
function(keys, values, rr){ return sum(values); }
|
でまとめます。
rereduce がいつ起こるのか?というのは非常に難しい問題です。内部のbtreeの構造に依存し、完全にrereduceの発生を予測してロジックを制御するのは困難です。したがって、reduce を実装するときには、reduce 内では結合則を満たさないロジックが含まれるときは十分に注意してください。例えば、足し算、引き算と、かけ算や割り算が混じるようなreduce関数を書くときは要注意です。
|
|
そして、HTTP GETを使用して結果を取得できます。
$ curl –X GET http://localhost:5984/couchiki_development/_view/calc/count_tag
{“rows” : [{“key”; null, “value” : 310}]}
|
先ほどとおなじくすべてのタグの数は310となっています。ここで、XMLのタグの数をカウントしてみるには、key=%22XML%22 を使用します。
$ curl -X GET \
http://localhost:5984/couchiki_development/_view/calc/count_tag?key=%22XML%22
{"rows" : [{"key"; null, "value" : 13}]}
|
これだけであれば、Mapでも結果の配列の要素数を数えれば簡単に取得できるでしょう(ただし、reduceによって転送量が大きく削減されます)。group=true を指定すると、Key毎にカウントをまとめて取得することができるので、当初の目的であったタグクラウドのためのカウント数の取得には便利です。
$ curl -X GET \
http://localhost:5984/couchiki_development/_view/calc/count_tag?group=true
{ "rows" : [
{ "key" : "Architecture", "value" : 21}.
{ "key" : "developerWorks Japan”, "value" : 106},
….
]}
|
今回説明した以外にも、いくつかのURIクエリパラメーターが使用可能になっています。どのようなものが利用可能かは公式サイトのWikiで確認してください。
クエリ処理のデバッグ
最後に、クエリ処理のデバッグ方法を示します。現時点では、CouchDBのクエリ処理のデバッグに有効な方法は逐一処理の内容をログにはき出すことです。mapおよびreduce用に定義した関数内では、log(object)という関数が使用できます。例えば、
map : function(doc){
log(doc);
...
}
|
のような形で書いておくと、docの完全な内容がCouchDBのログファイル(/usr/local/var/log/couchdb/couchdb.log)に出力されますので、tail などのコマンドで出力を追いながらデバッグするのが最も有効な方法の様です。
まとめと次回予告
今回は、CouchDB のビュー/クエリ機能であるMapReduceフレームワークの利用方法を学びました。これで、CouchDBに対するCRUD操作のほとんどすべてを習得したことになります。次回は、より便利に使うためのJSONドキュメントの記述方法などを学びます。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| サンプル・コード | couchiki_b.zip | 141KB | HTTP |
|---|
参考文献
著者について  | |  | 佐々木 庸平 ITスペシャリスト
日本アイ・ビー・エム システムズ・エンジニアリング株式会社 テクノロジーイノベーション
2004 年 日本 IBM システムズ・エンジニアリング(株)入社。システム管理製品の技術支援を経て、2006 年より Web を中心とした先進技術発掘、および実プロジェクト適用への技術支援の活動に従事。 |
 | |  | 小出 理史 ITスペシャリスト
日本アイ・ビー・エム システムズ・エンジニアリング株式会社 テクノロジーイノベーション
2002年日本アイ・ビー・エム システムズ・エンジニアリング(株)入社。グリッド・コンピューティング、RESTなどを担当。 |
記事の評価
|