2011年12月21日水曜日

FuelPHPってなんじゃ?(インストール編)

PHPのフレームワークは数々ありますが、どれも巨大すぎたり機能が足りなすぎたり、丁度よいモダンなフレームワークはなかなかありませんでした。
そこに登場したCodeIgniterは機能が豊富ですっきりとしている良質なモダンフレームワークとして人気なようです。

今回のFuelPHPはCodeIgniterの開発者なども参加しており、CodeIgniterや他のプロダクトのいいとこどりの期待のフレームワークだそうです。


主な特徴は
・MVC
・HMVC(画面要素の複数のMVCモジュールをひとつに纏めて1画面をつくれる)
・モジュール拡張可能
・セキュリティ対応(出力エンコーディング、入力フィルタ、XSS、CSRF、SQLインジェクション)
・豊富なコマンドライン
・ORMとSQLどちらもOK
・認証機構つき
・複数のビューテンプレート(smartyなど)に対応
・scaffoldできる
・migrateできる
・PHPUnitできる

こう見ると分かる通り、Railsから多くの影響を受けているようです。

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

ここでは、
ドキュメントルートを/var/www/html
アプリベースを/opt/cloudpack/
として、blogアプリをつくってみたいと思います。


インストール
$ curl get.fuelphp.com/oil | sh
$ cd /opt/cloudpack/
$ oil create blog
これだけでひと通りのセットが揃います。

次にセキュリティのためにpublicフォルダだけをDocumentRootに移動します。
# mv blog/public /var/www/html/blog

そして、index.phpに書いてある、アプリパスを変更します。
# vi /var/www/html/blog/index.php
~
/**
 * Path to the application directory.
 */
define('APPPATH', realpath('/opt/cloudpack/blog/fuel/app/').DIRECTORY_SEPARATOR);

/**
 * Path to the default packages directory.
 */
define('PKGPATH', realpath('/opt/cloudpack/blog/fuel/packages/').DIRECTORY_SEPARATOR);

/**
 * The path to the framework core.
 */
define('COREPATH', realpath('/opt/cloudpack/blog/fuel/core/').DIRECTORY_SEPARATOR);


それでは画面をみてみます。



おー、Welcome画面が表示されてました。
とりあえず問題ないようです。



Scaffolding

次にデータベースの設定をします。(mysqlサーバーがインストールされ起動していることを前提とします)

vi /opt/cloudpack/blog/fuel/app/config/db.php
'default' => array(
                'type'        => 'mysql',
                'connection'  => array(
                        'hostname' => 'localhost',
                        'port' => '8889',
                        'database' => 'blog',
                        'username' => 'memorycraft',
                        'password' => '*********',
                        'persistent' => false,
                ),
                'identifier'   => '`',
                'table_prefix' => '',
                'charset'      => 'utf8',
                'caching'      => false,
                'profiling'    => false,
        ),

vi /opt/cloudpack/blog/fuel/app/config/development/db.php
'default' => array(
                'connection'  => array(
                        'dsn'        => 'mysql:host=localhost;dbname=blog',
                        'username'   => 'memorycraft',
                        'password'   => '***********',
                ),
        ),


mysqlでデータベースとユーザーを作成します。
mysql> create database blog;
mysql> GRANT ALL PRIVILEGES ON blog.* TO 'memorycraft@localhost'  IDENTIFIED BY '*******';


次に、scaffoldをつくってみます。
$ cd /opt/cloudpack/blog/
$ oil g scaffold blog title:string body:string tags:string created_at:date

そしてmigrate
$ oil refine migrate

これで、データベースblogにblogテーブルが作成されました。
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| blog               |
| mysql              |
| performance_schema |
| test               |
+--------------------+
5 rows in set (0.00 sec)


それでは画面をみてみます。



ブログの管理画面ができています。
入力して

登録すると、、、、



おおー!一覧に保存内容がひょうじされました。
railsと同じノリですね。

あくまでひな形ですが、このくらいのスピードでDB、画面ができあがります。
PHPはもうこれでいいんじゃないかなと思うくらいの可能性を感じます。

これから使い込んでみたいと思います。
以上です。

2011年12月14日水曜日

SESってなんじゃ?(SMTPがサポートされました)

SESにSMTP機能がサポートされました。
メーラーでもSendmailやPostfixでも利用できるのは素晴らしいですね。
今回は試しにmiura@cloudpack.jpのメールアドレスで、GmailからSESを経由してHotmailにメール送信してみたいと思います。


SESの設定


AWSコンソールのSESタブを開くと、「SMTP Settings」というメニューが増えています。


これを開くと、SMTPサーバーの基本情報が表示されます。
Server Name: email-smtp.us-east-1.amazonaws.com
Port: 465
TLS: Yes
Authentication: Your SMTP credentials

Authenticationの欄にある通り、SMTPで送信するには、SMTP認証用のIAMユーザーを作る必要があり、画面中央の「Create My SMTP Credential」を押すと手順に従って設定できます。


ボタンを押すと、自動的にIAMタブに遷移し、「Create User for SMTP」というダイアログが開き、SMTP用のIAMユーザーを登録します。ここにSMTP用のIAMユーザー名を入力して、「Create」ボタンを押します。



すると、作成完了のダイアログが表示されます。
Credential情報は「Show User SMTP Security Credential」で表示されます。


Credential情報はファイルとして「Donwload Credential」でダウンロードし保管しておきます。
"Iam User Name","Smtp Username","Smtp Password"
"smtp-memorycraft","ACCESS_KEY******","SECRET_KEY******"


ダウンロードが終わり、ダイアログを閉じると再びSESタブに自動遷移します。
SESのダッシュボードを表示し、メール送信者を追加するために、「Verify a New Sender」をクリックします。



ここで、今回SMTPで送信したい送信者のメールアドレスを入力し、「Submit」をクリックます。



すると、そのメールアドレス宛に認証用のメールが届くので、認証リンクをクリックします。



AWSの画面が表示され、認証が完了した旨のメッセージが表示されています。これで送信者の認証が完了しました。


SESは無償では、認証したメールアドレスに対してしか送信できないため、テスト受信用のメールアドレスもこれと同様にして認証しておきます。(手順は同様なので、割愛します。)



メールクライアント( Gmail ) の設定


次にGmailのSMTP設定を行います。 「設定<アカウントとインポート」 タブの「名前」欄で送信者のメールアドレスを追加します。今回は既にGmail経由送信で設定済みだったので、「編集」で設定を変えてみます。



「次のステップ」を押すと、「Gmail経由で送信する」が選択されているので、「SMTPサーバー経由で送信する」のほうをクリックして選択します。すると、SMTPサーバー、ポート、ユーザー名、パスワード、接続プロトコルの入力欄が表示されます。
SESのSMTP Settingsに記載のあったように、SMTPサーバー名とポート、プロトコルを入力します。

Server Name: email-smtp.us-east-1.amazonaws.com
Port: 465
TLS: Yes
Authentication: Your SMTP credentials

また、ダウンロードしたcredential.csvに記載されている、Smtp UsernameとSmtp Passwordをユーザー名、パスワードに入力します。



ここで、「変更を保存」をクリックすると、有効性のチェックが始まるのですが、下記のようなエラーがでました。



どうやらTLS接続だとうまく設定できないようです。接続プロトコルをSSLにしたら、設定が成功したのでこのまま進みます。
登録が完了すると、「名前」欄が以下のような表示に変わり、AWSを使ったSMTP接続が設定されていることが確認できます。




それでは実際にメールを送信してみます。以下の内容のメールを「Verify User」で登録しておいたhotmailアドレス宛に送信します。



hotmail側を見てみると、、、


おおー、受け取れています!
アプリケーションなどでもSDKではなく、従来のSMTP送信でメール配信することができますね。

以上です。

2011年12月9日金曜日

S3ってなんじゃ?(SDKで複数S3オブジェクトを一括削除:SDKバグの修正パッチつき)

S3で複数のS3オブジェクトを指定して一括で削除できるようになりました。
早速PHPのSDKで試してみたいと思います。

delete_objectsというメソッドが追加されていますので、これを使ってみます。
対象のバケットには以下のようにファイルがあります。


ここで、以下のようなプログラムでvp_sample.csvとwelcome.txtを削除するコードを書きます。
$ vi bulk_delete.php
-------------------------------------------
#!/usr/bin/php -q
<?php
require_once '/opt/aws/php/default/sdk.class.php';

$bucket = 'hoge-bucket';

$s3 = new AmazonS3('AccessKey','SecretKey');
$s3->set_region(AmazonS3::REGION_APAC_NE1);

$response = $s3->delete_objects ($bucket, array(
        'objects' => array(
                array('key' => 'vp_sample.csv'),
                array('key' => 'welcome.txt')
        )
));
echo "done."
?>


これを実行します。

$ php bulk_delete.php
PHP Notice:  Undefined property: AmazonS3::$multi_object_delete_xml in /opt/aws/php/sdk-1.4.8/services/s3.class.php on line 1406
PHP Fatal error:  Uncaught exception 'Exception' with message 'String could not be parsed as XML' in /opt/aws/php/sdk-1.4.8/services/s3.class.php:1406
Stack trace:
#0 /opt/aws/php/sdk-1.4.8/services/s3.class.php(1406): SimpleXMLElement->__construct('')
#1 /opt/cloudpack/test/bulk_delete.php(21): AmazonS3->delete_objects('hoge-bucket', Array)
#2 {main}
  thrown in /opt/aws/php/sdk-1.4.8/services/s3.class.php on line 1406

。。。。。
エラーになりました。

なんでしょう?
/sdk-1.4.8/services/s3.class.php を調べてみると、delete_objectsメソッド内でSimpleXMLObjectの初期化に失敗しているようです。

1406行目の
$xml = new SimpleXMLElement($this->multi_object_delete_xml);

ここで、初期化の引数に渡している
$this->multi_object_delete_xml
がどこにも存在していないことが原因のようです。SDKのバグですね。
これを直してみます。

ここで、まず238行目あたりに、
public $multi_object_delete_xml;

とクラスのプロパティを追加し、

さらに238行目あたり、__construct内で、他のXMLのタグ文字列の初期化と同じように、以下のように初期化します。
$this->multi_object_delete_xml = '<?xml version="1.0" encoding="utf-8"?><MultiObjectDelete></MultiObjectDelete>';


これで、再度実行してみます。
$ php bulk_delete.php
done.

おお、成功しました。

実際削除されたのか確認します。

いけてますね。

この修正パッチをAWSのフォーラムにアップしましたので、必要な方は自己責任でご利用ください。
またパッチ内容に問題があればご一報いただけると助かります。

Bug: PHP SDK 1.4.8 Multi Object Delete (patch)
https://forums.aws.amazon.com/thread.jspa?threadID=81967&tstart=0

以上です。

追記:2011/12/21
このバグは1.4.8.1で修正されたようです。
https://forums.aws.amazon.com/thread.jspa?threadID=81967&tstart=0&messageID=299617#299617

2011年11月30日水曜日

シェルってなんじゃ?(mysqlに簡単ランダムデータ生成)

DBにランダムな大量データを入れ込みたいけれども、データの内容やテーブル同士の結合などあまり気にしなくて良い場合は、ちゃんとしたジェネレーターなどをインストールするよりもシェルでさくっと書いてしまいたい時があります。

ここではmysqlにシェルからINSERTし続けるシェルを書いてみたいと思います。

例として対象となるテーブルは以下のとおり

CREATE TABLE user (
 id int(11) NOT NULL AUTO_INCREMENT,
 name varchar(50) DEFAULT NULL,
 age int(11) DEFAULT NULL,
 description text,
 created_at datetime DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB

 ここで、idはオートインクリメント、created_atはNOW()を使用するのでいいとして、name, age, descriptionにランダムなデータを入れ込むためにランダム生成をします。


文字列のランダム

mkpasswdというパスワード生成用のランダム文字列の生成ツールで、expectパッケージに含まれています。
たとえば3文字以上の数字と5文字以上の大文字が必ず含まれる50文字のランダム文字列が生成するには以下のようにします
$ mkpasswd -l 50 -d 3 -C 5
kDVoDyxqwd5csu0bfuk8dpoygyhwwersqoazoycvehsovsMjqS


mkpasswdは厳密にランダムさを求めたり使用文字のルールを決めることができるので便利ですが、とても重いので大量に生成するには向かない場合があります。 ほかにもuuidgenというUUIDを作成するコマンドがあります。
これは全世界的に一意なIDを発行するためのツールです。
$ uuidgen
a62f9c8d-9d97-4b5d-a572-d83222837fc2


数値のランダム

数値をランダムに出力するにはexprが便利です。
たとえば1〜8までのランダム数値は以下のようにします。
$ expr 1 + $RANDOM % 8
3

これらをつかってランダムなデータをテーブルに入れるスクリプトを書いてみます。
mysqlへの投入はmysql -e "SQL文"で、コマンドから直にSQL実行できるので、それを利用します。


ランダムデータ生成スクリプト

insert_random.sh
#!/bin/sh

while ((1))
do
name=$(uuidgen | tr -d '-' | cut -c 1-10)
desc=$(mkpasswd -l 50 -s 0)
age=`expr 1 + $RANDOM % 1000`
mysql -u hoge_user cloudpack -phoge_pass -e "INSERT INTO user(name, age, description, created_at) VALUES('${name}', ${age}, '${desc}', NOW());"
echo -n "."
done;

exit 1


これを実行します。
$ sh ./insert_random.sh 
...............................................................................................................................................................................................................................................................................................................................................................................................

進捗がすすんでいるようです。
ちょっと別のターミナルセッションからデータを覗いてみます。

mysql> select * from user;
+----+------------+------+----------------------------------------------------+---------------------+
| id | name       | age  | description                                        | created_at          |
+----+------------+------+----------------------------------------------------+---------------------+
|  1 | 598d056309 |  896 | rkggeRoxaqvmhlcwhon2ordjhafdLoqdrs5ecaplouaqkccdfk | 2011-11-30 03:00:14 |
|  2 | 6ab7bf0029 |  487 | 8iOrolcltandneoxtsmwizx6shtreovoodulnmpjePdqgyrxxa | 2011-11-30 03:00:14 |
|  3 | c62df8dab5 |  464 | oxncixqdamctmeppmohmwkdayxzgw0rxRvwx3vbDhlyndqnlcv | 2011-11-30 03:00:14 |
|  4 | 63a9d01e41 |  609 | ilovfkcskrvdui2nHnumcfarlkn5iVaopabyymiafjiystbyvx | 2011-11-30 03:00:14 |
|  5 | e5abb35d2d |  677 | epkdylnwhlx9kophdOxcvcofqqheuljknsiivgon8cyovyAvaf | 2011-11-30 03:00:15 |
|  6 | 2888a4f6fd |  230 | ywqbCazysbakzweqfohli6oqhjuncohtyxplxp2Frcuhfacyqa | 2011-11-30 03:00:15 |

〜

| 2452 | 2b1e84b98d |  388 | kEheuvccvwusygulkfhig6qvnffxmnveahazmnaclcIbllcz5j | 2011-11-30 03:00:52 |
| 2453 | ea1ef37248 |   44 | feoeumwokgrvtypatzxfyfZg3ltvrnxwdEhzfveveykavitzl9 | 2011-11-30 03:00:52 |
| 2454 | 05a5341597 |  309 | chlpvjttq2axlvkrubjgubfdfhwzoatolbfddo6nqpgWgbkfPh | 2011-11-30 03:00:52 |
| 2455 | 8403fbafe9 |  655 | tdwi2egdtAucxroylarikvqdfhpxnwxeopqxtcufvqsix0Wekz | 2011-11-30 03:00:52 |
| 2456 | 471be293bc |  660 | lnKqpbueti3izbg1orceykiomjqocexezpkulqgkdhzFczyfze | 2011-11-30 03:00:52 |
| 2457 | 05628b4a7c |  180 | ueebrCqwn5brvjUqrkwcpg6qpjpkokkrtxvfczxjtcyqmrfpnf | 2011-11-30 03:00:52 |
| 2458 | 3688ea54b3 |   92 | wrpamv9QmycbccpuwqPnlsjdyiwcl5avyfgytdqllgtiljcwsc | 2011-11-30 03:00:52 |
+------+------------+------+----------------------------------------------------+---------------------+
2458 rows in set (0.01 sec)

おお、順調に投入されています。
このように、気軽に大量のデータをつくれ、また常にデータ投入が行われている状態にすることで開発の最初から負荷を気にすることもできます。
以上です。

統計ってなんじゃ?(mysqlで度数分布)

プロジェクトを運用していると、ユーザーのサービス利用傾向を知るためにデータの統計を依頼されることがあります。

たとえば、ゲームを例にすると、ユーザーがどのアイテムを何個持っているか、と聞かれた場合、一人ひとりのユーザーの持ち物をしらべるとなると、結局DBのダンプになってしまいます。
そういった場合、「何個もっているユーザーが何人いる」ということを調べることで、ユーザーの全体的な動向を知ることができます。

ここでは、0〜9個の人は何人、10〜19個の人は何人、、、というように段階的に範囲を絞って、アイテムごとにMySQLから統計を採ってみたいと思います。このような統計の採り方を度数分布といいます。

対象となるテーブルは

●ユーザー
mysql> select id, name, age, created_at from user limit 5;
+----+------------+------+---------------------+
| id | name       | age  | created_at          |
+----+------------+------+---------------------+
|  1 | 598d056309 |  896 | 2011-11-30 03:00:14 |
|  2 | 6ab7bf0029 |  487 | 2011-11-30 03:00:14 |
|  3 | c62df8dab5 |  464 | 2011-11-30 03:00:14 |
|  4 | 63a9d01e41 |  609 | 2011-11-30 03:00:14 |
|  5 | e5abb35d2d |  677 | 2011-11-30 03:00:15 |
+----+------------+------+---------------------+
5 rows in set (0.00 sec)

●アイテム
mysql> select id,name from item;
+----+--------------------------+
| id | name                     |
+----+--------------------------+
|  1 | やくそう                 |
|  2 | どくけしそう             |
|  3 | ばんそうこう             |
|  4 | 100円ライター         |
|  5 | ビー玉                   |
|  6 | りんご                   |
|  7 | けしごむ                 |
|  8 | わゴム                   |
+----+--------------------------+
8 rows in set (0.00 sec)


●ユーザーの持ち物
mysql> select * from my_item limit 5;
+------------+---------+---------+---------------------+
| my_item_id | item_id | user_id | created_at          |
+------------+---------+---------+---------------------+
|          1 |       7 |     330 | 2011-11-30 01:33:17 |
|          2 |       6 |      68 | 2011-11-30 01:33:17 |
|          3 |       7 |     477 | 2011-11-30 01:33:17 |
|          4 |       5 |     554 | 2011-11-30 01:33:17 |
|          5 |       4 |     153 | 2011-11-30 01:33:17 |
+------------+---------+---------+---------------------+
5 rows in set (0.00 sec)

だとします。

アイテムごとに各ユーザーが持っている個数を取得するには以下のようにとれます。
select item_id, user_id, count(my_item_id) from my_item group by item_id, user_id;

ここで、例えば0〜9個までを持っているユーザーは、更にhaving句を使って以下のように絞られます。
select item_id, user_id, count(my_item_id) from my_item group by item_id, user_id having count(my_item_id) >= 0 AND count(my_item_id) < 10

オンラインゲームなどは保持数の上限などを設けている場合がほとんどです、例えばアイテムは最大100個しか
持てないという制限があるとします。
ここでは、アイテムの所持数を0〜9、10〜19、、、90〜100のように区切って、段階ごとに範囲を決めて抽出するとすると、以下のSQLになります。

mysql< select 
z.item_id,
z.name,
IFNULL(t1.cnt, 0) as "0=<10",
IFNULL(t2.cnt, 0) as "10=<20",
IFNULL(t3.cnt, 0) as "20=<30",
IFNULL(t4.cnt, 0) as "30=<40",
IFNULL(t5.cnt, 0) as "40=<50"
from
(select 
 id as item_id,
 name
 from 
 item
) z
LEFT OUTER JOIN
(
select a.item_id,count(a.user_id) as cnt from (select item_id, user_id, count(my_item_id) from my_item group by item_id, user_id having count(my_item_id) >= 0 AND count(my_item_id) < 10) a group by a.item_id
) t1 ON t1.item_id = z.item_id
LEFT OUTER JOIN(
select a.item_id,count(a.user_id) as cnt from (select item_id, user_id, count(my_item_id) from my_item group by item_id, user_id having count(item_id) >= 10 AND count(item_id) < 20) a group by a.item_id
) t2 ON t2.item_id = z.item_id
LEFT OUTER JOIN(
select a.item_id,count(a.user_id) as cnt from (select item_id, user_id, count(my_item_id) from my_item group by item_id, user_id having count(item_id) >= 20 AND count(item_id) < 30) a group by a.item_id
) t3 ON t3.item_id = z.item_id
LEFT OUTER JOIN(
select a.item_id,count(a.user_id) as cnt from (select item_id, user_id, count(my_item_id) from my_item group by item_id, user_id having count(item_id) >= 30 AND count(item_id) < 40) a group by a.item_id
) t4 ON t4.item_id = z.item_id
LEFT OUTER JOIN(
select a.item_id, count(a.user_id) as cnt from (select item_id, user_id, count(my_item_id) from my_item group by item_id, user_id having count(item_id) >= 40 AND count(item_id) < 50) a group by a.item_id
) t5 ON t5.item_id = z.item_id;
+---------+--------------------------+-------+--------+--------+--------+--------+
| item_id | name                     | 0=<10 | 10=<20 | 20=<30 | 30=<40 | 40=<50 |
+---------+--------------------------+-------+--------+--------+--------+--------+
|       1 | やくそう                 |  1336 |      0 |     16 |     52 |     21 |
|       2 | どくけしそう             |  1346 |      0 |     13 |     60 |     16 |
|       3 | ばんそうこう             |  1327 |      1 |     14 |     47 |     27 |
|       4 | 100円ライター         |  1355 |      0 |     18 |     47 |     24 |
|       5 | ビー玉                   |  1368 |      0 |     14 |     52 |     24 |
|       6 | りんご                   |  1334 |      1 |     21 |     49 |     19 |
|       7 | けしごむ                 |  1354 |      1 |     18 |     54 |     17 |
|       8 | わゴム                   |  1329 |      1 |     23 |     42 |     23 |
+---------+--------------------------+-------+--------+--------+--------+--------+


少し冗長になってしまいましたが、もっと他にスマートなやり方があるのかも知れません。 他にいい方法も浮かばなかったのですが、とりあえず抽出することができました。

本日は以上です。

2011年11月24日木曜日

シェルってなんじゃ?(sedとsortでアクセスログのソート)


Apacheなどのアクセスログで複数のホストの同一日付のファイルをAWSTATSなどにかけることがあります。
その場合logresolvemergeなどを利用するのですが、logresolvemergeは各ファイルが内部でソートされていることを前提としています。
ログは書きこみプロセスなどの関係で時折内部で日時が前後することがあるため、ファイルの内部をソートしなおす必要があります。

スクリプト言語などのループなどを利用してもできるのですが、ここではシェルを使った方法を紹介したいと思います。

使うコマンドは
  • cat
  • sort
  • sed
です。

たとえばログの中身が
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET / HTTP/1.1" 304 -
のようになっているファイルが数十個あるとします。

ここでは例としてhoge1.log, hoge2.logの以下の2ファイルをサンプルとして使用します。
$ cat hoge1.log
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -

$ cat hoge2.log
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -


cat
まずこれらのファイルを連結します
$ cat *
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
連結しただけなのでまだ順番は変わりません。

つぎにその出力を普通にソートしてみます。
sortコマンドには以下のようにセパレータで区切られたフィールドと範囲指定でソートキーを優先順に複数定義することができる-kオプションがあります。

以下は、区切り文字を半角スペースとして1行をフィールド分割したときの2番目のフィールドの2文字目から3文字目を第一ソートキー、1番目のフィールドを第2ソートキーとすることを表します。

$ cat test.log
b A03z 2
a A12f 3
a B03d 1

$ sort -t " " -k 2.2,2.3 -k 1,1 test.log
a B03d 1
b A03z 2
a A12f 3

これを利用して以下のログファイルをソートしてみます。

$ cat hoge1.log
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -

$ cat hoge2.log
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -

$ cat * | sort -t " " -k 4.9,4.12 -k 4.5,4.7 -k 4.2,4.3 -k 4.14,4.15 -k 4.17,4.18 -k 4.20,4.21
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827

うまくいきませんでした。
これはログを出力したマシンのロケールの問題で、月のフィールドが3文字コードになっているため、文字列では大小を比較できないためです。

こういった場合のためにsortには、-Mオプションが用意されており、Jan < ... <  Decという解釈ができるようになっているのですが、残念ながらフィールドとして独立していないと機能しないようです。またそれ以外のソートキーは文字列として解釈したいので、このオプションは結局使用できませんでした。


sed
仕方ないので、Jan〜Dec を 01〜12に置換してからsortする必要があるようです。
そこでsedを利用します。

sedは文字列の置換が得意なプログラムです。
ここでは/Nov/ を /11/と置換するようにします。

$ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:"
125.54.146.207 - app [21/11/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/12/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/11/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/04/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/11/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/05/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/11/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/10/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/03/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -

sedではこのように-eオプションを連結することで、個々の置換条件を複数適用することができます。また、パラーんの最初のsの直後をセパレータとみなすので、/以外でもセパレータとして使用することができ、パターン内に/がある場合などに便利です。


sort
それではこの結果をソートにかけるようにパイプで連結してみます。
$ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:" | sort -t " " -k 4.8,4.11 -k 4.5,4.6 -k 4.2,4.3 -k 4.13,4.14 -k 4.16,4.17 -k 4.19,4.20
125.54.146.207 - app [22/11/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/11/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [11/03/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/04/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [27/05/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/10/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [05/11/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/12/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -

うまくソートできました。

sed
これでもいいのですが、月がソート用に数字変換されたままですので、最後に元に戻してあげます。
$ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:" | sort -t " " -k 4.8,4.11 -k 4.5,4.6 -k 4.2,4.3 -k 4.13,4.14 -k 4.16,4.17 -k 4.19,4.20 | sed -e "s:/01/:/Jan/:" -e "s:/02/:/Feb/:" -e "s:/03/:/Mar/:" -e "s:/04/:/Apr/:" -e "s:/05/:/Apr/:" -e "s:/06/:/Jun/:" -e "s:/07/:/Jul/:" -e "s:/08/:/Aug/:" -e "s:/09/:/Sep/:" -e "s:/10/:/Oct/:" -e "s:/11/:/Nov/:" -e "s:/12/:/Dec/:"
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [27/Apr/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -

これで完成です。


2011年11月18日金曜日

実験くん(jQueryでグラフィカルEC2コンソール)

前回の記事でEC2まわりの構成の視覚化をしましたが、今回はさらに発展させてEC2まわりのサービスの結合をしてみました。



やったことは
  • EC2、SGに加え、ELB、EBSのサービスを追加しました
  • サービスはDrag&Dropで移動できます
  • 移動した位置はCookieで記憶しています 
  • 各サービスの中見出しはアコーディオンです 
  • 参照だけではなく、実際のサービス接続を操作できます 
  • EC2とELB,EBSの間は未使用端点からのDrag&Dropで結合ができます 
  • コネクタをダブルクリックするとサービス結合を切断します
などです。

今回ソースは長いので割愛して、実際に操作したところをスクリーンキャストで紹介したいと思います。


vimeoのサイトで直接みるとHD画質で再生できます。

AWS Plug by jQuery
http://vimeo.com/32302745

思った以上に構成がわかりやすくなり、操作も直感的にできたかと思います。
もっといじったら面白い事になりそうです。

本日は以上です。

2011年11月10日木曜日

実験くん(jsPlumbでAWSの構成を可視化)

jsPlumbという、HTMLのエレメント同士をコネクタで接続できるライブラリがあります。

このライブラリはjQueryのプラグインとしてもリリースしているので、これを使用してEC2とセキュリティグループの関係を接続図としてHTMLレンダリングするように実験してみました。 


EC2とセキュリティグループの情報をjsonで出力するPHPを以下のように用意します。


jsPlumbはGoogleCodeでホストされているので、以下からダウンロードします。

jsPlumb
http://code.google.com/p/jsplumb/downloads/list

次に、htmlページを用意し、phpからjsonを取得し、jsplumbに適用させます。

これだけで終わりです。

さっそく実行してみます。

おおー!
うまく表示されました!

はぐれセキュリティグループが一目でわかりますね。


jsplumbはjQueryUI.Draggableをサポートしているので、コネクタで接続されたエンドポイント(ノード)はドラッグで移動することも出来ます。



ELBの接続状況など、いろいろなことが可視化できそうです。

以上です。

2011年10月31日月曜日

SESってなんじゃ?(AWSコンソールにSESが登場)

今回はSESについてです。
アプリケーションを開発する際、ユーザーにメールを一斉送信するような場合が多い時に、メールサーバーの負荷やパフォーマンスに対するコストは忘れがちですが意外とかかり、そのため外部のメール配信専用のサービスを利用することも多いかと思います。
SESは一括メール送信のためのサービスで、EC2でEメールを配信する際には是非つかいたいサービスです。


そのSESがAWSコンソール上で管理できるようになりました。
早速触ってみたいと思います。


AWSコンソールを開くと、右側にSESのタブが表示されていることがわかります。
SESタブを選択すると、SESのダッシュボードが表示されます。



まずは、メールの送信者のメールアドレスを登録します。
「DashBoard」から「Verify a New Sender」のボタンを押すと、下のようにダイアログが表示されるので、送信者のEメールアドレスを入力して「Submit」を押します。


すると、入力したメールアドレスに、確認用のメールが送られる旨のメッセージが表示されます。


AWSサービスから、入力したメールアドレス宛に送信者確認のリンクが記載されたメールが届くので、リンクをクリックしてメールアドレスの実在確認を行います。


リンクをクリックすると、AWSサービスの画面がブラウザに表示され、認証が完了します。


AWSコンソールに戻り、左ペインの「Verified Senders」メニューをクリックすると、右ペインに先ほど登録した送信者のアドレスが表示されています。



 送信者アドレスの右に「Send Test Email」というテスト送信が出来るリンクがあるのでクリックしてみます。
すると、テスト送信用のダイアログが表示されます。
SESでのテスト送信は登録した送信者宛にしか送信できません。
ここでは、先ほどの認証された送信者アドレスの一覧から送信先を選択します。
また、件名と本文を入力し、「Send Test Email」をクリックします。


無事送信者アドレスに、メールが届きました。


 アプリケーションで動的にユーザーに送信するときなど、登録されていないメールアドレスに送信する場合には、「Dash Board」から、「Request Production Access」のボタンを押し、プロダクション環境での利用申請を行うことで、未登録アドレスへの送信が可能になります。





 また、「Dash Board」でメールの使用状況などの統計がわかり、管理しやすくなりました。
本日は以上です。


2011年10月27日木曜日

シェルってなんじゃ?(ワンライナー編1 全文検索)

日々お世話になっている便利なシェルのワンライナーを紹介していきたいと思います。
このシリーズが出たときはネタに困ったときだと思ってください。。


以下は、特定のディレクトリ以下のパターンにマッチしたすべてのファイルから全文検索し、結果を色付けするワンライナーです。

find ディレクトリ -name "パターン" -print | xargs grep "検索ワード" --color=auto

実際に実行してみます。

find ./ -name '*.txt' -print | xargs grep "pack" --color=auto
./src/git/node/CMakeLists.txt:find_package(PythonInterp 2 REQUIRED)
./src/git/node/CMakeLists.txt:# package
./src/git/node/CMakeLists.txt:include("cmake/package.cmake")
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/__init__.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/S3Uri.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/Progress.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/AccessLog.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/Utils.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/CloudFront.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/PkgInfo.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/S3.py
./src/s3cmd-1.0.1/files.txt:/usr/lib/python2.4/site-packages/S3/SortedDict.py
....

おおっ。
色もついて便利ですね。

以上です。

2011年10月21日金曜日

SQSってなんじゃ?(遅延送信、一括送信機能が追加)


SQSに新機能が登場しました。今回追加されたのは、大きく分けて

  • メッセージの一括送信、削除
  • キューとメッセージの遅延送信

の2つです。

さっそく試してみましょう


キューとメッセージの遅延送信

遅延送信機能はコンソールから確認できるので、コンソールを見てみます。
まずはキューの遅延送信設定を確認してみます。
コンソールのSQSから「Create New Queue」ボタンでキューを作成します。

すると、下記のキュー設定ダイアログが表示されます。
赤枠の部分に「Delivery Delay」があり、ここで最大15分まで遅延時間を設定できます。
たとえばここで遅延時間を45秒に設定すると、このキューで送信したメッセージはデフォルトで送信後45秒後から受信可能になります。



次に、そのキューで「SendMessage」してみると、メッセージ送信ダイアログが表示されます。
赤枠のとおり、メッセージでも個別に遅延時間を設定できます。
デフォルトでは、キューで設定した遅延時間がセットされています。なにもせずに送信すると、キューの遅延設定時間で遅延し、ここで別の時間にセットすると、その時間だけ遅延します。

ここでは3分に設定して、メッセージ送信してみます。




ここで、「Queue Action」から「View/Delete Messages」を選び、キューのメッセージ一覧ダイアログが表示され、「Start Polling Messages」を数回押してみますが、しばらくは(3分以内)メッセージが受信されません。




3分経過すると、先程のメッセージが受信されます。メッセージ情報のSentとFirst Receivedの間が3分以上あいているのが確認できます。




また、メッセージダイアログで何も設定せずに送信すると、キューで設定した45秒間は受信できず、45秒経過後に受信されるようになります。



このようなメッセージの遅延は、仕様やアプリケーションの他の処理との兼ね合いなどで必要になるシーンがあるので、バッチなどのバックエンド処理には重宝しそうです。



メッセージの一括送信、削除



APIにSendMessageBatchDeleteMessageBatchという関数が用意されました。メッセージの送信、削除を同時に10メッセージまで操作できるようです。ただし、通常のSendMessageなどは1つのメッセージ容量が65KBまででしたが、この一括送信では10個で65KBまでという制限があるようです。
この機能の利点は、10個までのメッセージの操作を1リクエストで行えるため、リクエストごとの課金を抑えることができます。


現時点(2011/10/21 18:00)では、メッセージの一括送信、削除の機能は、APIまでの対応でSDKではまだ未サポートのようです。

SQSのAPIリファレンス
http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/

こちらはSDKがサポートされたらまた報告したいと思います。


以上です。




2011年10月20日木曜日

WebSocketってなんじゃ?(Node編4 MongoDBってなんじゃ?)

今回はMongoDBの話題です。
MongoDBはいわゆるNoSQLで、BSONというJSONのバイナリ版のような形式でデータの入出力を行い、クエリなどのデータ操作をjavascriptで行うため、サーバーサイドJavascriptと非常に相性がよく、node.jsとともによく利用されているようです。

今回はこのMongoDBをnode.jsから操作してみます。

MongoDB公式サイトにあるように、yumでインストールするために、MongoDB用のyumリポジトリを追加します。
以下のファイルを作成します。
/etc/yum.repos.d/10gen.repo
[10gen]
name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686
gpgcheck=0


さっそく、yumでインストールします。
yum install mongo-10gen mongo-10gen-server -y


インストールが終了したら、mongodが/etc/init.d/以下にできるので、起動します。
/etc/init.d/mongod start


MongoDBのコンソールを立ち上げてみます。
$ mongo

MongoDB shell version: 2.0.0
connecting to: test
testというdbにつながったようです。
helpと入力すると、いくつかのコマンドが紹介されています。DBの一覧を見てみます。
> show dbs
local (empty)
test (empty)
localとtestというDBがあり、中身はまだ空になっています。

MongoDBは基本的にはスキーマを定義することなく、いきなりデータを保存できます。データが保存された時点でそれがスキーマになります。
以下のように、hogeというデータを保存、その直後にhogeを参照してみます。
> db.hoge.save({id:"1", name:"HOGE"});
>
> db.hoge.find();
{ "_id" : ObjectId("4e9f8ced316d58abc218dc43"), "id" : "1", "name" : "HOGE" }

このように、クエリにあたるものもすべてjavascriptで行い、結果はJSON形式で表示されます。いかにもnodeと相性がよさそうです。


それでは、このMongoDBにnode.jsからアクセスしてみます。
node.jsからMongoDBにアクセスするには、MongoDB用のパッケージモジュールを入れる必要があります。
Mongo用のモジュールはいくつかあるのですが、今回はMongooseを利用してみます。
mongooseはnpmでインストールします。
# npm install -g mongoose

> mongodb@0.9.6-22 install /usr/local/lib/node_modules/mongoose/node_modules/mongodb
> bash ./install.sh

================================================================================
=                                                                              =
=  To install with C++ bson parser do <npm --mongodb:native="" install="" mongodb="">   =
=  the parser only works for node 0.4.X or lower                               =
=                                                                              =
================================================================================
Not building native library for cygwin
Using GNU make
mongoose@2.3.5 /usr/local/lib/node_modules/mongoose
├── colors@0.5.0
├── hooks@0.1.9
└── mongodb@0.9.6-22


以下のようにmongoにデータを投入するコードを書いてみます。
#!/usr/local/bin/node

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/memorycraft');

var Schema = mongoose.Schema;
var ItemSchema = new Schema({title:String, auther:String, price:Number, date:Date});
var Item = mongoose.model('Item', ItemSchema);

var item = new Item();
item.title = "体脂肪計タニタの社員食堂 ~500kcalのまんぷく定食~";
item.auther = "タニタ";
item.price = 1200;
item.date = new Date(2010, 1, 21);

item.save(function(err){
  if(err){
    console.log(err.message);
  }
  else{
    console.log("保存しました");
  }
});


これを実行します。
$ ./hello_mongo.js
保存しました


ここで、実際にデータが投入されているか再度mongoのコンソールで確認してみます。
$mongo
> show dbs
local (empty)
memorycraft 0.0625GB
test 0.0625GB
>
> use memorycraft
switched to db memorycraft
>
> show collections
items
system.indexes
>
> db.items.find()
{ "date" : ISODate("2010-02-20T15:00:00Z"), "price" : 1200, "auther" : "タニタ", "title" : "体脂肪計タニタの社員食堂 ~500kcalのまんぷく定食~", "_id" : ObjectId("4e9f98394699a3fb40000001") }

memorycraftというdbが増えていて、投入したデータが入っていることが確認できました。

ここで、mongodb+nodeがいいのは、データの保持の時点でJSONなので、取得結果もJSONになっていることです。
そして、上記のコードを書いたようにアクセスの結果がすべてコールバックで記述できるので、とても柔軟にコード記述ができます。
例えば以下のように別途WEBフレームワークなどを使用した場合、取得したデータをそのままレスポンスに返すことで余計な変換処理が必要なくなります。
Item.find({}, function(err, items) {
  if(!err) {
    res.json(items);
  }
});

ほんの少し触れただけですが、とても便利なことがわかりました。
まだまだいろいろいじってみようと思います。
本日は以上です。

2011年10月19日水曜日

WebSocketってなんじゃ?(Node編3 node.jsでサーバーサイドjQuery)

再びnodeの話題です。 前回紹介したSocket.IOのように、node.jsにはnpmで管理される多数のパッケージモジュールがあり、それらをインストールすることでnodeの機能を拡張します。

今回は、jsdomというモジュールを使用してみます。
これはDOMのJS実装を、モジュール化したもので、node上でJSによるDOM解析と操作をサーバーサイドで行うことが可能です。

それでは早速試してみます。
まずnpmでjsdomをインストールしてみます。
$ npm install -g jsdom

> contextify@0.0.6 preinstall /usr/local/lib/node_modules/jsdom/node_modules/contextify
> node-waf clean || true; node-waf configure build

Nothing to clean (project not configured)
Setting srcdir to                        : /usr/local/lib/node_modules/jsdom/node_modules/contextify 
Setting blddir to                        : /usr/local/lib/node_modules/jsdom/node_modules/contextify/build 
Checking for program g++ or c++          : /usr/bin/g++ 
Checking for program cpp                 : /usr/bin/cpp 
Checking for program ar                  : /usr/bin/ar 
Checking for program ranlib              : /usr/bin/ranlib 
Checking for g++                         : ok  
Checking for node path                   : not found 
Checking for node prefix                 : ok /usr/local 
'configure' finished successfully (0.026s)
Waf: Entering directory `/usr/local/lib/node_modules/jsdom/node_modules/contextify/build'
[1/2] cxx: src/contextify.cc -> build/Release/src/contextify_1.o
[2/2] cxx_link: build/Release/src/contextify_1.o -> build/Release/contextify.node
Waf: Leaving directory `/usr/local/lib/node_modules/jsdom/node_modules/contextify/build'
'build' finished successfully (0.789s)
jsdom@0.2.8 /usr/local/lib/node_modules/jsdom 
├── htmlparser@1.7.3
├── cssom@0.2.0
├── request@2.1.1
└── contextify@0.0.6_
インストールが完了しました。


確認のために、自ら作成したDOMの一部を抽出するスクリプトを実行してみます。

hello_dom.js
#!/usr/local/bin/node

var jsdom    = require("jsdom").jsdom;
var document = jsdom("<html><head></head><body><div id='greeting'>hello world</div></body></html>"),
var window   = document.createWindow();

console.log(window.document.getElementById("greeting").innerHTML);
実行
$ chmod 755 hello_dom.js
$ ./hello_dom.js
hello world
うまく出力されました。

様々な言語にDOMを操作するライブラリがありますが、最も有名で最も使いやすいのはやはりjQueryだと思います。
ここではせっかくjavascriptを使っているので、jQueryを利用してみます。

jsdomはjQueryを実行するときに便利なenvメソッドを用意しています。 envメソッドは、ロードしたHTMLソースにscriptタグを追加し、そのscriptがDOMにロードされた後に引数のコールバック関数を呼び出します。
以下のスクリプトは、memorycraftのブログ(このブログです)のトップページから、記事のタイトルをjQueryで抽出してコンソールに出力する、いわゆるスクレイピングを行います。

hello_jquery.js
#!/usr/local/bin/node

var jsdom = require("jsdom");

jsdom.env("http://www.memocra.blogspot.com", [
  'http://code.jquery.com/jquery-1.5.min.js'
],
function(errors, window) {
  var $ = window.$;
  $("h3 a").each(function(){
    console.log($(this).text());
  });
});
実行
$ chmod 755 hello_jquery.js
$ ./hello_jquery.js
EC2でMySQL(世界編2 Spiderとレプリケーションで高速負荷分散)
EC2でMySQL(世界編1 Spiderとレプリケーションで世界進出)
SQSってなんじゃ?(AWSコンソールにSQS登場)
S3ってなんじゃ?(サーバー側でファイルのAES256暗号化)
WebSocketってなんじゃ?(Node編2 Socket.IOでプッシュ通信)

おお!うまく出力できました。

このようにjQueryをサーバーサイドで利用することで、クライアントとサーバーサイド両側で同じ方法でHTMLをコントロールすることができ、とても便利です。

WebSocket関係なくなっちゃいましたが、本日は以上です。

2011年10月14日金曜日

EC2でMySQL(世界編2 Spiderとレプリケーションで高速負荷分散)

前回のつづきです。

前回はSpiderとレプリケーションで、リージョン間での高速なデータ分散を紹介しました。前回で少し触れましたが、この構成ではたとえばEU側のDBにJPのデータの書き込みには対応できません。

それを解決し、どちらのリージョンからもすべての商品を投入できるようにしてみたいと思います。
構成は以下のとおりです。



SpiderノードのDBで、書き込みと読み込みでそれぞれ別々のテーブルを用意し、別々のデータノードにそれぞれシャーディングを行います。

データノードの作成とレプリケーションの設定は前回と同じなので割愛します。

Spiderノード

spider_ja
CREATE TABLE item_r (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
CONNECTION=' table "item", user "remote_user", password "remote_pass" '
PARTITION BY LIST (region_id) (
  PARTITION ja_ja VALUES IN (1) COMMENT = 'host "111.111.0.1", port "3306"',
  PARTITION ja_eu VALUES IN (2) COMMENT = 'host "111.111.0.2", port "3306"'
)
;

CREATE TABLE item_w (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
CONNECTION=' table "item", user "remote_user", password "remote_pass" '
PARTITION BY LIST (region_id) (
  PARTITION ja_ja VALUES IN (1) COMMENT = 'host "111.111.0.1", port "3306"',
  PARTITION eu_eu VALUES IN (2) COMMENT = 'host "222.222.0.2", port "3306"'
)
;

spider_eu
CREATE TABLE item_r (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
CONNECTION=' table "item", user "remote_user", password "remote_pass" '
PARTITION BY LIST (region_id) (
  PARTITION eu_ja VALUES IN (1) COMMENT = 'host "222.222.0.1", port "3306"',
  PARTITION eu_eu VALUES IN (2) COMMENT = 'host "222.222.0.2", port "3306"'
)
;

CREATE TABLE item_w (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
CONNECTION=' table "item", user "remote_user", password "remote_pass" '
PARTITION BY LIST (region_id) (
  PARTITION ja_ja VALUES IN (1) COMMENT = 'host "111.111.0.1", port "3306"',
  PARTITION eu_eu VALUES IN (2) COMMENT = 'host "222.222.0.2", port "3306"'
)
;


item_wに対して書き込んでみます。
mysql> INSERT INTO item_w (name, shop_id, region_id) VALUES('おいしい水', 1, 1), ('おしゃれなバッグ', 2, 1), ('Cool Watch', 3, 2),('Cute Ring', 4, 2);
Query OK, 4 rows affected (1.95 sec)
Records: 4  Duplicates: 0  Warnings: 0


書き込みが成功しました。
item_wで参照すると、
mysql> select * from item_w;
+----+--------------------------+---------+-----------+
| id | name                     | shop_id | region_id |
+----+--------------------------+---------+-----------+
|  1 | おいしい水          |       1 |         1 |
|  2 | おしゃれなバッグ |       2 |         1 |
|  3 | Cool Watch               |       3 |         2 |
|  4 | Cute Ring                |       4 |         2 |
+----+--------------------------+---------+-----------+
4 rows in set (1.96 sec)
正しく投入されていることがわかりますが、レスポンスはやはり悪いです。

そこでitem_rで参照してみます。
mysql> select * from item_r;
+----+--------------------------+---------+-----------+
| id | name                     | shop_id | region_id |
+----+--------------------------+---------+-----------+
|  1 | おいしい水          |       1 |         1 |
|  2 | おしゃれなバッグ |       2 |         1 |
|  3 | Cool Watch               |       3 |         2 |
|  4 | Cute Ring                |       4 |         2 |
+----+--------------------------+---------+-----------+
4 rows in set (0.01 sec)
高速で読み込みできています。

これで、どちらのリージョンからも全リージョン用のデータの書き込みができ、なおかつ読み込みスピードを維持することが出来ました。

書き込み時は他方のリージョンにSpiderでアクセスするので、少し時間がかかります。
前回のように基本はJPのデータはJPリージョンから投入するようにルール化すれば書き込みスピードが損なわれることはありません。
すべてmicroインスタンスでの確認ということもあり、もしかしたらSpiderのサーバーやテーブルパラメータをいじることで他リージョンへのSpiderアクセスも速くなるかもしれません。それは今後の課題にしたいと思います。

以上です。

2011年10月13日木曜日

EC2でMySQL(世界編1 Spiderとレプリケーションで世界進出)

ふたたびSpiderの話題です。

WEBアプリを世界展開する場合、要件の1つとして、世界各国のユーザーは全てのデータを参照する必要があるが、データの登録は各国、地域の担当者が各自のデータを登録したい、というケースが多いと思います。

たとえば、商品のサイトで、EUと日本にそれぞれ管理者がいるとします。
その場合のユースケースとしては、

  • EUの管理者はEUの商品を登録する
  • 日本の管理者は日本の商品を登録する
  • ユーザーは全ての商品を閲覧できる

となります。

このとき、DBがどのリージョンにあるかによって、ユーザーや管理者の間で、レスポンススポードに差がでます。
DBを日本に置くとEUのユーザーや管理者には遅いシステムとなってしまい、EUに置くとその逆になります。
そのため、各リージョンに全てのリージョンで登録した商品データを用意し、ユーザーのロケーションに関わらず、DBアクセスをユーザーと同じリージョン内で済ませる必要があります。管理者がアクセスするDBも管理者のいるリージョン内で済ませる必要があります。




これをSpiderとレプリケーションで実現してみます。
上図をみると想像できるかも知れませんが、方法としてはEUとJPそれぞれのリージョンに属する商品マスタに対して書き込み、EUではJPの、JPはEUのスレーブをもち、それぞれをSpiderでシャーディングします。
DBの図で表すと、下記のようになります。


それでは実際に試してみます。

データノード

CREATE TABLE item (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
;


Spiderノード

spider_ja
CREATE TABLE item (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
CONNECTION=' table "item", user "remote_user", password "remote_pass" '
PARTITION BY LIST (region_id) (
  PARTITION ja_ja VALUES IN (1) COMMENT = 'host "111.111.0.1", port "3306"',
  PARTITION ja_eu VALUES IN (2) COMMENT = 'host "111.111.0.2", port "3306"'
)
;

spider_eu
CREATE TABLE item (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(256) DEFAULT NULL,
  shop_id int(11) DEFAULT NULL,
  region_id int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (id, region_id)
) ENGINE=SPIDER DEFAULT CHARSET=utf8
CONNECTION=' table "item", user "remote_user", password "remote_pass" '
PARTITION BY LIST (region_id) (
  PARTITION eu_ja VALUES IN (1) COMMENT = 'host "222.222.0.1", port "3306"',
  PARTITION eu_eu VALUES IN (2) COMMENT = 'host "222.222.0.2", port "3306"'
)
;


レプリケーション

ja-eu
$ mysql -u root
mysql> CHANGE MASTER TO
  MASTER_HOST='222.222.0.2',
  MASTER_USER='remote_user',
  MASTER_PASSWORD='remote_user',
  MASTER_PORT=3306;

eu-ja
$ mysql -u root
mysql> CHANGE MASTER TO
  MASTER_HOST='111.111.0.1',
  MASTER_USER='remote_user',
  MASTER_PASSWORD='remote_user',
  MASTER_PORT=3306;


※各ノード間のアクセス権限やサーバーIDの登録、セキュリティグループの設定などは以前の記事に書いたので割愛します

ポイントはregion_id(1:ja, 2:eu)のLISTパーティションにするため、プライマリキーをidとregion_idの複合キーにするところです。INSERT時には、登録したリージョンの region_idをセットします。
※登録したリージョンではないregion_idをセットするとパーティションに含まれず、シャーディング対象にならないので、注意が必要です。

spider-ja
mysql> INSERT INTO item (name, shop_id, region_id) VALUES('おいしい水', 1, 1), ('おしゃれなバッグ', 2, 1);
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0


spider-eu
mysql> insert into item(name,shop_id,region_id) values('Cool Watch', 3, 2),('Cute Ring', 4, 2);
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0

すると、
それぞれのspiderノードですべてのitemがselectできます。
mysql> select * from item;
+----+--------------------------+---------+-----------+
| id | name                     | shop_id | region_id |
+----+--------------------------+---------+-----------+
|  1 | おいしい水          |       1 |         1 |
|  2 | おしゃれなバッグ |       2 |         1 |
|  1 | Cool Watch               |       3 |         2 |
|  2 | Cute Ring                |       4 |         2 |
+----+--------------------------+---------+-----------+
4 rows in set (0.01 sec)


このように、リージョンが分かれていても、アクセス速度を気にすること無くシャーディングができました。
以上です。

2011年10月7日金曜日

SQSってなんじゃ?(AWSコンソールにSQS登場)

SQSがAWSコンソールで利用可能になりました。 いままでキューに入ったメッセージを確認する手段がプログラム経由だったので、管理がしづらかったのですが、これでメッセージの状態がわかりやすくなると思います。 AWSコンソールを開くと、まずSQSのタブが増えていることに気づきます。
SQSタブをクリックすると、SQSのキュー一覧画面が表示され、いままで作ったキューが表示されているのがわかります。




まずはキューの作成をしてみます。



「Create New Queue」ボタンをクリックします。
すると、キュー作成のダイアログが開きます。


ここで、
  • Queue Name:キュー名
  • Default Visibility Timeout:読み込み後、他のクライアントから秘匿する秒数のデフォルト値、メッセージ送信時に指定しないとこれが使用される
  • Message Retention Period:メッセージの保持期間
  • Maximam Message Size:メッセージの最大サイズ
を指定して、「Create Queue」ボタンをクリックします。
すると、キュー一覧画面にもどり、キューのメッセージ数が1つ増えているのがわかります。


キューのメッセージを確認するには、キューを選択して、「Queue Action」から「View/Delete Messages」を選択します。
すると、メッセージ詳細ダイアログが立ち上がります。



「Start Polling Messages」を押すと、メッセージのフェッチが始まります。下のプログレスバーが終了すると全てのメッセージがチェックされ、一覧に表示されます。またフェッチを途中で中止した場合は右上の「Stop Now」を押します。
メッセージの詳細を見たい場合は、メッセージの「 More Details」を押すとメッセージ全文と付帯情報を確認できます。



キューの一覧にもどり、下部ペインの「Permission」タブで、各キューのパーミッションを設定できます。


ここでは各アクションごとに細かく権限ポリシーを設定でき、また「Edit Policy Document」ボタンでポリシーファイルのJSON的な書き方でも設定が可能です。

いままでプログラムで送信したキューの状態を知るにはそのプログラムを書かないといけないため、なんとなくブラックボックスになりがちだったSQSですが、このように、可視化されることでSQSがぐっと身近に感じられるようになり、管理も簡単になりました。とても助かります。

本日はここまで。

2011年10月5日水曜日

S3ってなんじゃ?(サーバー側でファイルのAES256暗号化)

S3がサーバーサイドでのファイル暗号化に対応しました。

ファイルのアップロード時、"x-amz-server-side-encryption"ヘッダに"AES256"を指定することで暗号化が自動的に行われ、配信時に自動的に複合してくれるそうです。 

まずはコンソール上で試してみます。
 AWSコンソールのS3タブで「Upload」ボタンを押し、アップロードダイアログを使って、普通にファイルを選択します。

「Set Details..」で次の画面を表示すると、RSSのチェックボックスの下に、「Use Server Side Encryption」という項目が追加されているのがわかります。


これにチェックを入れて、あとは通常通りアップロードします。
これだけでファイルが暗号化されて保持されます。簡単ですね。

次は、APIを利用して暗号化をしてみます。
ドキュメントを見るかぎり現在のところ、REST、Java、.NETで対応しているようです。
ここではJavaのSDKでの例を紹介します。
AmazonS3 s3client = new AmazonS3Client(
  new BasicAWSCredentials("XXXXXXXXXXX",  "YYYYYYYYYYY"));
 
File file = new File("hoge.txt");
PutObjectRequest putRequest = new PutObjectRequest("hoge-bucket", "hoge.txt", file);
            
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setServerSideEncryption( 
  ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION );     
putRequest.setMetadata(objectMetadata);

PutObjectResult response = s3client.putObject(putRequest);


このように、通常のアップロードリクエストにメタデータを追加してあげるだけです。
本日はここまで。

WebSocketってなんじゃ?(Node編2 Socket.IOでプッシュ通信)

前回Node編1で、スタンドアローンのHTTPサーバーをつくり、HTTP通信を行いました。

今回は、WebSocketを使用した通信を行ってみます。 WebSocketとは、ブラウザとサーバーの双方向通信のプロトコル規格で、現在標準化団体が仕様を策定中の技術です。chromeやsafariなど、いくつかのモダンブラウザがサポートしており、これが一般的に使用されると、いままでのサーバーアプリケーションの作り方が大きく変わってくる可能性がある、注目の技術です。

ここで使用するのは、Socket.IO というnode.jsのモジュールです。 node.jsはモジュールという単位でライブラリを管理しており、npmというモジュール管理ツールで簡単にいろいろなモジュールを追加削除できます。これはrubyでいうgemのようなものです。
Socket.IOはただnode.jsでWebSocketを実装しているだけではなく、モダンブラウザ以外のWebSocketを使用できないブラウザでも、AjaxやFlashなど他の代替手段があるかどうかを自動的に検知して通信方式を自動で選択されるので、ブラウザの対応をあまり気にしなくてもよくなっています。
また、Socket.IOは WebSocketとその代替手段において、クロスドメイン通信を可能にしています。

早速実践してみます。 まず最初にnpmをインストールします。
$ curl http://npmjs.org/install.sh | sh
$ npm install -g socket.io


socket.ioのインストールでエラーが出る場合がありますが、その場合はtarが古いので、tarを最新にしておきます。
$ cd /usr/local/src
$ wget http://ftp.gnu.org/gnu/tar/tar-1.26.tar.gz
$ tar xzvf tar-1.26.tar.gz
$ cd tar-1.26
$ ./configure
$ make
# make install


socket.ioをインストールします。
$ curl http://npmjs.org/install.sh | sh
$ npm install -g socket.io

※エラーになる場合は curl -L http://npmjs.org/install.sh | sh とやると良いようです。

これで実装の準備が整いました。
それでは実装を始めます。
今回は、メッセージを送信したら見ている全員にメッセージを表示させる、簡単なチャットの仕組みを作ってみます。

サーバー側

/opt/memorycraft/node/socketsample.js
require.paths.push('/usr/local/lib/node_modules');

var server = require('http').createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('server connected');
});
server.listen(1337);

var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket) {
  socket.emit('info', { msg: 'welcome' });
  socket.on('msg', function (msg) {
    io.sockets.emit('msg', {msg: msg});
  });
  socket.on('disconnect', function(){
    socket.emit('info', {msg: 'bye'});
  });
});


サーバー側では任意のポート(ここでは1337)を開放したhttpサーバーにsocket.ioをつなげてリスン状態にします。
通信の確立(connection)や切断(disconnect)などのビルトインイベントや、自分のアプリケーションで使用したいオリジナルのイベント(ここではmsg)のイベントハンドラを定義します。
このコードでは接続したときに"welcome"、切断したときに"bye"をそのユーザーに、あるユーザーから"msg"というイベントが送られたら全員に"msg"というイベントを、それぞれプッシュ送信しています。

クライアント側

/var/html/www/test/node/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
  <script src="http://xxx.xxx.xxx.xxx:1337/socket.io/socket.io.js"></script>
  <script type="text/javascript">
  $(function(){
    var socket = io.connect('http://xxx.xxx.xxx.xxx:1337/');
    socket.on('connect', function() {
      $("#log").html($("#log").html() + "<br />" + 'connected');
      socket.on('info', function (data) {
        $("#log").html($("#log").html() + "<br />" + data.msg);
      });
      socket.on('msg', function(data){
        $("#log").html($("#log").html() + "<br />" + "<b>" + data.msg + "</b>");
      });
      $("#send").click(function(){
        var msg = $("#msg").val();
        if(!msg){
          alert("input your message");
          return;
        }
        socket.emit('msg', msg);
      });
    });
  });
  </script>
  <title>nodetest</title>
</head>
<body>
  <input id="msg" type="text" style="width:400px;"></input>
  <input id="send" type="button" value="send" /></br >
  <div id="log" style="width:400px;height:400px;overflow:auto;border:1px solid #000000;"></div>
</html>

クライアント側からはサーバー側の1337ポートに接続しますが、サーバー側は実際にファイルを自分で配置しなくてもSocket.IOが勝手にクライアント側のライブラリを/socket.io/socket.io.jsとして配信してくれます。
そのファイルをscriptファイルでロードするようにし、あとは接続部分とサーバーからのイベントのハンドラを定義します。また、ボタンを押したらテキストをサーバー側に送信するように記述します。


それでは、サーバー側のコードを実行してみます。
$ node /opt/memorycraft/node/socketsample.js
   info  - socket.io started

ブラウザでhtmlを表示します。

サーバー側からの"welcome"メッセージが表示されていることがわかります。
また、メッセージを入力して送信すると、

メッセージ欄にサーバーから送られてきているのがわかります。

別のブラウザでも開いておき、そちらからメッセージを送信するともう一方のブラウザにメッセージが表示されることが分かります。


このように、socket.ioを利用することで多くのブラウザで簡単にWebSocketを実現することができます。
WebSocketは通信内容が少なく、コネクションやリソースの消費が少ないので、ゲームやソーシャルサイトなどのマルチユーザーアプリや、クライアントのログ集計など、様々な分野での利用が期待できます。
今日はここまで。