ラベル dynamodb の投稿を表示しています。 すべての投稿を表示
ラベル dynamodb の投稿を表示しています。 すべての投稿を表示

2012年12月21日金曜日

AWS SDK for Node.js ってなんじゃ?

遅ればせながら、AWS SDK for Node.js が出ました。

簡単に触ってみます。

インストール

npm install aws-sdk


S3にアクセス

test.js
/*
AWSオブジェクトを初期化します。
*/
var AWS = require('aws-sdk');

/*
アカウント情報やリージョンなどを設定します。
*/
AWS.config.update({accessKeyId: 'xxxxxxxxxxxxx',
                   secretAccessKey: 'yyyyyyyyyyyyyyyyyyyyyyy',
                   region: 'ap-northeast-1'});

/*
 このようにしてもOKです。
 AWS.config.loadFromPath('./credentials.json');
*/


/*
S3オブジェクトを作ります。
*/
var s3 = new AWS.S3();

/*
バケットのオブジェクト一覧を取得するには以下のようにします。
*/
s3.client.listObjects({Bucket:"memorycraft-log"}, function(err,data){
  if(err){
    console.log(err);
  }
  else{
    for(var i=0;i<data.Contents.length;i++){
      console.log(data.Contents[i].Key);
    }
  }
});


基本的には、呼び出し→コールバックという一般的なnode.jsの作法ですね。
結果は以下のように正常に取得できます。

# node test.js
logs/
logs/20121221%3F--num-instances_$folder$
logs/20121221/
logs/20121221/ip-10-128-12-239-04_0.gz
logs/20121221/ip-10-128-12-239-04_1.gz
logs/20121221/ip-10-128-12-239-04_10.gz
logs/20121221/ip-10-128-12-239-04_2.gz
logs/20121221/ip-10-128-12-239-04_3.gz
logs/20121221/ip-10-128-12-239-04_4.gz
logs/20121221/ip-10-128-12-239-04_5.gz
logs/20121221/ip-10-128-12-239-04_6.gz
logs/20121221/ip-10-128-12-239-04_7.gz
logs/20121221/ip-10-128-12-239-04_8.gz
logs/20121221/ip-10-128-12-239-04_9.gz
logs/20121221/ip-10-128-12-239-04_98.gz
logs/20121221/ip-10-128-12-239-04_99.gz
logs/20121221/ip-10-157-2-63-04_0.gz
logs/20121221/ip-10-157-2-63-04_1.gz
logs/20121221/ip-10-157-2-63-04_10.gz
logs/20121221/ip-10-157-2-63-04_100.gz
logs/20121221/ip-10-157-2-63-04_2.gz
logs/20121221/ip-10-157-2-63-04_3.gz
logs/20121221/ip-10-157-2-63-04_4.gz
logs/20121221/ip-10-157-2-63-04_5.gz
logs/20121221/ip-10-157-2-63-04_6.gz
logs/20121221/ip-10-157-2-63-04_7.gz
logs/20121221/ip-10-157-2-63-04_8.gz
logs/20121221/ip-10-157-2-63-04_9.gz

Dynamoにアクセス
/*
AWSオブジェクトを初期化します。
*/
var AWS = require('aws-sdk');

/*
DynamoDBオブジェクトをつくります。
*/
var ddb = new AWS.DynamoDB();

/*
投入するデータは以下のようにします。
*/
var data = {
    id:{S:'msg_'+(new Date()).getTime()},
    msg:{S:'hoge'},
    datetime:{S:(new Date())}
};

/*
投入します
*/
ddb.client.putItem({
    TableName:'nodetest',
        Item:data
    },
    function(err, res) {
        if(err){
            console.log("Error [" + err.code + "] id=" + data.id.S);
        }
        else{
            console.log("Done id=" + data.id.S);
        }
});


結果は以下のようになります。
# node test.js 
Done id=msg_1356091073919


AWSコンソールでみると


正常に登録されたことがわかります。

他にもEC2とSWFにアクセスできますが、他のサービスはまだまだこれからのようです。
今後に期待ですね。

以上です。


2012年11月14日水曜日

Node.jsってなんじゃ?(DynamoDBにアクセス)

いままでチャットプログラムをサンプルとして使用していましたが、やりとりしているメッセージは基本的に揮発性のものです。サーバーが止まればなくなってしまう、もしくは参照できなくなるデータです。

今回は、AmazonDynamoDBをメッセージの保存場所としてみます。
node.js でDynamoDBを利用するには、dynodeというモジュールを使用します。

dynodeはDynamoDBへのAPIアクセスをラップしてnodeライクに使うことができます。

では早速触ってみたいと思います。
まずdynodeをインストールします。
# npm install -g dynode

ロードと設定は以下のように行います。
var dynode = require('dynode');
dynode.auth({region:"ap-northeast-1",
                accessKeyId:"xxxxxxxxxxxxxxxxxx",
                secretAccessKey:"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"});


また、各メソッドは以下のようにコールバック式で行います。
dynode.putItem("chat", {id:id, msg:msg, date:(new Date()).toString()}, function(err, res){
  console.log(err);
});


ここでは、メッセージの保存先として、idというプライマリキーと、dateをレンジキーをもつchatというテーブルを作成します。


サンプルとして以前のチャットプログラムを使用します。

server.js

connectionハンドラで、接続が確率したときにchatテーブルから過去のメッセージをスキャンしてarchiveイベントに乗せてクライアントにemitします。
また、msgハンドラでは、いままで単純にemitしていたところを、dynode.putItemで保存してからemitするようにしました。
var server = require('http').createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('server connected');
});
server.listen(3001);

var dynode = require('dynode');
dynode.auth({region:"ap-northeast-1",
                accessKeyId:"xxxxxxxxxxxxxxxxxx",
                secretAccessKey:"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"});

var io = require('socket.io').listen(server);
var RedisStore = require('socket.io/lib/stores/redis');
opts = {host:'10.0.0.200', port:6379};
io.set('store', new RedisStore({redisPub:opts, redisSub:opts, redisClient:opts}));

io.sockets.on('connection', function (socket) {

  var id = socket.id;
 //メッセージ履歴をscanします
  dynode.scan("chat", function(err, res){
  //dateでソートします
  res.sort(function(a,b){
     if(a.date < b.date)return -1;
      if(a.date > b.date)return 1;
      return 0;
    });
    //archiveイベントに送信します。
    io.sockets.emit('archive', { id:id, archive: res });
    io.sockets.emit('info', { id:id, msg: 'welcome '+id });
  });

  socket.on('msg', function (msg) {
    io.sockets.emit('msg', {id:id, msg: msg});<
              //保存します。
    dynode.putItem("chat", {id:id, msg:msg, date:(new Date()).toString()}, function(err, res){
        if(err){
            console.log(err);
            console.log(res);
        }
    });
  });
  socket.on('disconnect', function(){
    io.sockets.emit('info', {id:id, msg: 'bye '+id});
  })
});


client.js

クライアント側では、新たにarchiveハンドラを作成し、メッセージをまとめて展開できるようにしておきます。

$(function(){
    var socket = io.connect('http://'+hostname+':3001/');
    socket.on('connect', function() {
      $("#log").html($("#log").html() + "<br />" + 'connected');
      socket.on('info', function (data) {
        $("#log").html($("#log").html() + "<br />" + data.msg);
      });
      socket.on('archive', function(data){
                     //メッセージ履歴を表示します。
      for(var i=0;i<data.archive.length;i++){
      $("#log").html($("#log").html() + "<br />" + "<b>" +       data.archive[i].id + ":</b>" + data.archive[i].msg);
      }
      });

      socket.on('msg', function(data){
        $("#log").html($("#log").html() + "<br />" + "<b>" + data.id + ":</b>" + data.msg);
      });

      $("#send").click(function(){
        var msg = $("#msg").val();
        if(!msg){
          alert("input your message");
          return;
        }
        socket.emit('msg', msg);
      });
    });
  });


これで完了です。それでは動かして見ましょう。
以下のように、メッセージを投稿すると、



以下のようにDynamoに登録されているのがわかります。


ひとしきりメッセージを送ったあと、別のブラウザで接続すると、過去のメッセージが表示されるようになりました。


このように、nodeでもAWSに接続できるモジュールもいくつかあるので、幅が広がりますね。
以上です。

2012年1月25日水曜日

DynamoDBってなんじゃ?(SDK編)

前回は、AWSコンソールをつかってテーブルの作成まで行いました。
今回は、それらのテーブルにSDKをつかってアクセスしてみます。
ここでは、PHP SDKを使用してみます。

クラスの初期化
DynamoDBクラスの初期化は以下のように行います。
キーなどのパラメータは認証用のファイルに記載しておき省略することも可能です。
$ddb = new AmazonDynamoDB(
  array(
   'key' => 'xxxxxxxxxxxxxxx',
   'secret' => 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
   'default_cache_config' => '/tmp/cache'));

データの投入
次に、前回作成した2つのテーブルにそれぞれデータを投入してみます。
値の指定は、DynamoDBの型名とstring値を紐付けた形で投入するようです。
//book
$books = array(
array('1', '978-4274066306', 'Joel on Software'),
array('2', '978-0321413093', 'Implementation Patterns'),
array('3', '978-1244865723', 'Agile Software Development'),
);
for($i=0; $i<count($books); $i++){
  $ddb->put_item(array(
    'TableName' => 'book',
    'Item' => array(
      'id'   => array(AmazonDynamoDB::TYPE_NUMBER => (string)$books[$i][0]),
      'isbn' => array(AmazonDynamoDB::TYPE_STRING => (string)$books[$i][1]),
      'name' => array(AmazonDynamoDB::TYPE_STRING => (string)$books[$i][2]),
     )
  ));
}

//comment
$comments = array(
  array('1', strtotime('-10 days'), 'memorycraft', 'i like this'),
  array('1', strtotime('-7 days'), 'hoge', 'i never like this'),
  array('2', strtotime('-9 days'), 'moge', 'this is awesome'),
);
for($i=0; $i<count($comments); $i++){
  $ddb->put_item(array(
    'TableName' => 'comment',
    'Item' => array(
      'book_id'    => array(AmazonDynamoDB::TYPE_NUMBER => (string)$comments[$i][0]),
      'posted_at'  => array(AmazonDynamoDB::TYPE_NUMBER => (string)$comments[$i][1]),
      'username'   => array(AmazonDynamoDB::TYPE_STRING => (string)$comments[$i][2]),
      'message'    => array(AmazonDynamoDB::TYPE_STRING => (string)$comments[$i][3]),
    )
  ));
}

データアクセス
続いて、先程データを投入したテーブルに対して、値の取得を行なってみます。
Hashのプライマリキーで一意に取得できる場合は、下記のようにget_itemで行います。
//Hashプライマリキーで完全一致
echo 'get_item (1) ' .
print_r(
  $ddb->get_item(
    array(
      'TableName' => 'book',
      'Key' => array('HashKeyElement' => array(AmazonDynamoDB::TYPE_NUMBER => '1')
    )
  )->body->Item->to_json(),
  true
) . "\n";
出力は以下のようになります。
get_item (1) {"id":{"N":"1"},"isbn":{"S":"978-4274066306"},"name":{"S":"Joel on Software"}}

複合プライマリキーを使い、Hashキーを指定し、Rangeキーで絞るような場合は、queryを使用します。
//複合キーで検索
echo 'query ' .
print_r(
  $ddb->query(
    array('TableName' => 'comment',
      'HashKeyValue' => array(AmazonDynamoDB::TYPE_NUMBER => '1'),
      'ConsistentRead' => true,
      'RangeKeyCondition' => array(
        'ComparisonOperator' => AmazonDynamoDB::CONDITION_GREATER_THAN_OR_EQUAL,
        'AttributeValueList' => array(array(AmazonDynamoDB::TYPE_NUMBER => (string)strtotime('-8 days')))
      )
    )
  )->body->Items->to_json(),
  true
) . "\n";
出力は以下のようになります。
query {"0":{"book_id":{"N":"1"},"message":{"S":"i never like this"},"posted_at":{"N":"1326880572"},"username":{"S":"hoge"}}}

また、キー値以外で検索を行う場合は、scan関数を利用します。
//キー以外で検索
echo 'scan ' .
print_r(
  $ddb->scan(
    array(
      'TableName' => 'comment',
      'AttributeToGet' => array('username', 'message'),
      'ScanFilter' => array(
        'username' => array(
          'ComparisonOperator' => AmazonDynamoDB::CONDITION_EQUAL,
          'AttributeValueList' => array(
            array(AmazonDynamoDB::TYPE_STRING => 'memorycraft')
          )
        )
      )
    )
  )->body->Items->to_json(),
  true
) . "\n";
出力は以下のとおりです。
scan {"0":{"book_id":{"N":"1"},"message":{"S":"i like this"},"posted_at":{"N":"1326621372"},"username":{"S":"memorycraft"}}}

テーブルの作成
ちなみにテーブルの作成は以下のように行います。
//book
$ddb->create_table(array(
        'TableName' => 'book',
        'KeySchema' => array(
                'HashKeyElement' => array(
                        'AttributeName' => 'id',
                        'AttributeType' => AmazonDynamoDB::TYPE_NUMBER
                )
        ),
        'ProvisionedThroughput' => array(
                'ReadCapacityUnits' => 50,
                'WriteCapacityUnits' => 10
        )
));

//comment
$ddb->create_table(array(
        'TableName' => 'comment',
        'KeySchema' => array(
                'HashKeyElement' => array(
                        'AttributeName' => 'book_id',
                        'AttributeType' => AmazonDynamoDB::TYPE_NUMBER
                ),
                'RangeKeyElement' => array(
                        'AttributeName' => 'posted_at',
                        'AttributeType' => AmazonDynamoDB::TYPE_NUMBER
                )
        ),
        'ProvisionedThroughput' => array(
                'ReadCapacityUnits' => 50,
                'WriteCapacityUnits' => 10
        )
));

この際、テーブルの作成には時間が多少かかるので、テーブルを作成したあとに続けてデータ追加処理など行う場合は、以下のように待機します。
foreach(array('book', 'comment') as $table_name){
  $count = 0;
  echo $table_name . " : status ";
  do {
    sleep(1);
    $count++;
    $res = $ddb->describe_table(array(
      'TableName' => $table_name
    ));
    $status = (string)$res->body->Table->TableStatus;
    echo ($status === 'ACTIVE' ? $status . "\n" : '.');
  }
  while ((string) $res->body->Table->TableStatus !== 'ACTIVE');
}
出力は以下のようになります。
book : status ...........................ACTIVE
comment : status ACTIVE


テーブルの削除
テーブルの削除は以下のように行います。
foreach(array('book', 'comment') as $table_name){
        $ddb->delete_table(
                array('TableName' => $table_name)
        );
}


RDBとは違うので、データの構成などに工夫が必要ですが、RDBとの使い分けで非常に高い効果を発揮すると思います。
東京リージョンへの展開が待ち遠しいですが、US-Eastに立てたインスタンスからはRTTがないので、ハイパフォーマンスを体験できます。

以上です。

DynamoDBってなんじゃ?(AWSコンソール編)

AWSがDynamoDBというサービスをリリースしました。リリースペース早すぎですね。
DynamoDBは高可用で超柔軟なスケーラビリティをもつNoSQLサービスです。

あれ?SimpleDBはどうしたの?と思ってしまいますが、SimpleDBよりも更に柔軟でスケーラブルです。

DyanmoDBはSimpleDBと比べて以下のような利点があります。
  • SimpleDBでは、1つのドメイン(DBでいうテーブル)につき10GBというデータ量制限がありましたが、DynamoDBでは無制限で、ドメイン制限を超えた場合のスケーリングがアプリケーションレイヤーの設計にまで影響していたのが解消され、カスタマイズされたConsistentHashing法により自動的にスケーリングが行われます。
  • DynamoDBではスループットを設定できるため、アクセス数が増加しても低レイテンシが保証されます。
  • SimpleDBではマシン時間での課金でしたが、DynamoDBではスループットによる課金です。ユーザーはより費用の見積もりがしやすくなりました。
  • SimpleDBではすべてのアトリビュート(カラム)にインデックスが貼られていました。DynamoDBではシングルプライマリキーもしくはコンポジットプライマリキーにのみインデックスを貼ることで高パフォーマンスを図っています。


それではさっそく触ってみます。

AWSコンソールをみると、左はじにDynamoDBのタブが見えます。





これをクリックするとDynamoDBのダッシュボードが表示されます。
「Create Table」をクリックして、テーブルの設定を行います。




まず、テーブル名とプライマリキーを設定します。
プライマリキーは単一のHashキーかHashとRangeの複合キーが設定できます。
ここではbookというテーブル名でシングルHashキーを設定して「Continue」をクリックします。




次に必要なスループットを設定します。
Read CapacityとWrite Capacityの値を設定しますが、
それぞれ1秒に読み書きされる回数の見積もりと1アイテム(DBでいうところの行)の平均データサイズで決まります。
つまり

Read Capacity:1秒間に読み込みされる回数 × 1つのアイテムの平均サイズ
Write Capacity:1秒間に書き込みされる回数 × 1つのアイテムの平均サイズ

という計算のもと設定します。




また、「Help me calculate ,,,」というチェックを入れると、読み込み回数やデータの平均サイズなどを入力して求めることもできます。




「Continue」をクリックすると、Alarmの設定画面に移ります。
ここでは上記で設定したスループットの何割を超えたら通知するかを設定できます。設定したスループット以上の性能はでないため、これで性能を監視し、必要に応じてスループットをアップするような運用になります。




適宜設定して「Create Table」をクリックするとテーブルが作成されます。



これでテーブルの作成が終わりました。
作成されたテーブルが一覧画面に表示され、行を選択すると、プロパティ情報や現在のスループットやレイテンシなどが表示されています。
またCloudWatchのメトリクスやアラームの情報なども確認できます。



追加で、book_idとposted_atを複合ハッシュキーにもつcommentというテーブルも作成します。



とりあえずテーブルまでできました。
次回はSDKからDynamoDBにアクセスしてみます。

今回は以上です。