• WebGL+HTML5で3DCGを勉強してみた! Part1

    どうも。営業部の伊藤です。

    前回のブログで書いたように、今更感は否めないですが…最近WebGLの勉強をしています。
    まだ超々初心者レベルまでしか勉強できていないですが、これはなかなか頭の体操になります。
    今まであまり経験した事のない思考が必要という事もあり、抜け毛の本数が増えそうではありますが…初心者レベルになれるまでは頑張ろうと思います。

    という事で、復習も兼ねてWebGLを利用した3Dグラフィックの描画方法について書こうと思います。
    ※3DCG、WebGLに着目しているため、実装で利用するHTML、JavaScript、数学的知識の基礎については省かせていただきます。

    3DCGのサンプルは以下をご覧ください。

    ※ピカピカしているのは描画遅延ではないです。チカチカしたらすいません。。。

    【補足情報】
     利用しているオープンソースは以下2種類となります。
     ○Google webgl-utils.js
      setIntervalやsetTimeoutだと色々と問題が多い(複数オブジェクトの描画に向いていない、処理の効率化ができない(GPUに優しくない))ので、上記再帰処理はwebgl-utilsを利用しています。

     ○glMatrix.js
      3DCGを表現するために必要となる行列計算用ライブラリとして利用しています。
      後述しますが、STEP6の座標変換行列を生成&通知のところで利用します。

     勉強に利用しているサイトは以下になります。
     ○wgld.org
      初心者向けに基礎から細かくレクチャーしてくれており、非常に分かりやすいです。オススメ!!
      
    それではサンプルをもとに実装内容を書いていきます。
    3DCG描画までのSTEPは以下のようになります。

    STEP1 canvasエレメントを取得
    STEP2 canvasからWebGLコンテキストを取得
    STEP3 シェーダを生成&コンパイル
    STEP4 モデルデータを用意
    STEP5 モデルデータからVBO(VertexBufferObject)を生成&バインド
    STEP6 座標変換行列を生成&通知
    STEP7 描画命令
    STEP8 STEP6~7の再帰処理(canvasをレンダリング)

    それでは早速STEP1から処理の内容から確認していきます。

    ■STEP1(canvasエレメントを取得)
    WebGLを利用した3DレンダリングはHTML5で策定されたcanvasタグ上で動きますので、まずはHTML内のcanvasエレメントを取得します。
    今回の場合、以下部分でエレメントを取得しています。

    var canvas = document.getElementById(“cube”);

    ■STEP2(canvasからWebGLコンテキストを取得)
    次に、描画処理用オブジェクトとして、STEP1で取得したcanvasエレメントからWebGLコンテキストを取得します。
    このコンテキストで各種描画処理や属性の設定をするため、ここでコンテキストが取得できなれけばWebGLを利用できないブラウザという事になります。
    今回の場合、以下部分でコンテキストを取得しています。

    gl = c.getContext(“webgl”) || c.getContext(“experimental-webgl”);
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.enable(gl.DEPTH_TEST);

    canvasに”webgl”という名前のコンテキストを要求しますが、仕様が策定中の時は失敗してしまうため”experimental-webgl”という名前のコンテキストも要求します。(失敗時用)
    その後、コンテキスト内部を初期化(clearColor)し、黒色に塗りつぶしています。
    3行目のenable(gl.DEPTH_TEST)はコンテキストに深度(DEPTH)を計算(TEST)しておけよ!という命令です。
    近くにあるものが遠くにあるものを隠すためにも描画の前に深度を把握しておく必要があります。

    サクサク進みますね。
    以外と簡単なのでは?と僕もここまでは今までの知識で問題なく吸収できたのですが、STEP3から急に難しくなり髪の毛が数本抜けたのを覚えています。。。

    ■STEP3(シェーダを生成&コンパイル)
    まず3D関係の知識が無い人はここで壁にぶるつかるでしょう。
    「シェーダ」ってなに?
    ウィキペディア先生には以下のように記載されています。

    主にライティング(光源計算)・シェーディング(陰影処理)とレンダリング(ピクセル化)を実行するためにグラフィックリソースに対して使用するソフトウェア命令の組み合わせである。
    「shade」とは「次第に変化させる」「陰影・グラデーションを付ける」という意味で、「shader」は頂点色やピクセル色などを次々に変化させるもの(より具体的に、狭義の意味で言えば関数)を意味する。
    ※wikipedia内の文章を引用

    難しい………

    超々初心者なりに要約します(間違えていたらすいません)。
    ソース内のコメントにも記載しておりますが、シェーダには大きく以下2種類のシェーダ存在します。

    1. バーテックスシェーダ(頂点シェーダ)
    2. フラグメントシェーダ(ピクセルシェーダ)

    【バーテックスシェーダ(頂点シェーダ)】
     3次元描画はポリゴンで表示する事が多く、ポリゴンは一般的に三角形の面をつなげたものが使われます(四角形を利用する事もあります)。
     三角形が利用されるポイントは、三角形は頂点の位置さえ決めれば面の位置やサイズが確定するという点です。
     四角形、五角形、、、それ以上の多角形も頂点だけでは面の位置やサイズを決める事はできません。
     この頂点の位置を計算するのがバーテックスシェーダ(頂点シェーダ)です。
     ※バーテックスシェーダは頂点の位置情報だけ管理する訳ではなく、頂点に関する”全ての情報(頂点の法線、テクスチャ座標、頂点色など)”を管理します。

    【フラグメントシェーダ(ピクセルシェーダ)】
     バーテックスシェーダ(頂点シェーダ)で頂点の位置や形状を決め、その後ラスタライズ(着色前準備)まで終えると、次にオブジェクトへの「着色」が必要になります。
     この着色内容を計算、決定する役割を担うのがフラグメントシェーダ(ピクセルシェーダー)です。
     色の計算だけであれば簡単に思えますが、これがそうでもないんです。
     3次元を表現するため、陰影、深度、光源、重複(オブジェクトやテクスチャの重なり)、乗算(透過時の後ろのテクスチャ色)などを考慮した着色計算が必要です。
     よって、上記のような色を計算するため、バーテックスシェーダ(頂点シェーダ)の情報が不可欠になります。
     このシェーダ連携についてはSTEP6で書かせていただきます。

    シェーダの生成&コンパイルは以下部分で実施しています。

    var shader;
    var shaderElement = document.getElementById(id);
    if (!shaderElement) {
    return null;
    }

    switch(shaderElement.type){
    case “x-shader/x-vertex”:
    shader = gl.createShader(gl.VERTEX_SHADER);
    break;
    case “x-shader/x-fragment”:
    shader = gl.createShader(gl.FRAGMENT_SHADER);
    break;
    default:
    return null;
    }

    //子ノード取得
    var source = “”;
    var childNodes = shaderElement.firstChild;
    while (childNodes) {
    if (childNodes.nodeType == 3) {
    source += childNodes.textContent;
    }
    childNodes = childNodes.nextSibling;
    }
    gl.shaderSource(shader, source); // ソース割り当て
    gl.compileShader(shader); // コンパイル

    見慣れないエレメントタイプがありますね。

    「x-shader/x-vertex」
    「x-shader/x-fragment」

    JavaScriptファイルではなく、HTMLファイルのheader部分も見てみてください。
    この2つのキーワードが上記で説明したバーテックスシェーダ(頂点シェーダ)とフラグメントシェーダ(ピクセルシェーダ)です。

    <script id=”vertex” type=”x-shader/x-vertex”>
    attribute vec3 vertexPosition;
    attribute vec2 vertexTexture;
    uniform mat4 vertexMatrix1;
    uniform mat4 vertexMatrix2;
    varying vec2 textureCoord;

    void main(void) {
    gl_Position = vertexMatrix1 * vertexMatrix2 * vec4(vertexPosition, 1.0);
    textureCoord = vertexTexture;
    }
    </script>
    <script id=”fragment” type=”x-shader/x-fragment”>
    precision mediump float;
    uniform sampler2D fragmentTexture;
    varying vec2 textureCoord;

    void main(void) {
    gl_FragColor = texture2D(fragmentTexture, vec2(textureCoord));
    }
    </script>

    中途半端にはなりますが…
    ここから説明が一気に難しくなる&細かくなり始めるので今回のブログはここまでにします。

    ではでは。


  • HTML5+WebGLのゲームが主流に!?WebRTCでKinectのようなゲームやソーシャルゲーム内のサムネイルが動画に!?

    どうも。営業部の伊藤です。

    今日は11/7です。
    先週末の日曜日は11/4です。
    そうなんです!そうです。
    3~4ヶ月前から着々と準備を進めていた、
    初ハーフマラソンの日でした。

    本来であればハーフマラソンを走った感想を書く予定でしたが、、、

    そうです。そうなんです!
    風邪をひいて参加できなかったんです。
    めちゃめちゃ楽しみにしていたハーフマラソンの日に体調不良がピークを迎え、自宅で引きこもりだったんです。
    よって、ハーフマラソンの感想は書けない事になりました。。。
    ※因みにブログを投稿した今日も回復していない状況の為、1週間以上長引いています。
    皆さんも体調管理には十分にご注意ください。

    そんなこんなで1日中引きこもりをしている中、9月頃FacebookのCEOであるザッカーバーグが発言した言葉を思い出しました。

    HTML5を過大評価したのは我々の最大の失敗

    ザッカーバーグが発言したように、まだまだスマートフォンアプリケーションの実装方法には課題を多く残しており、どこの企業もトライ&エラーの日々を繰り返しています。
    この発言だけ真に受けるとHTML5によるアプリケーションの実装方法はネイティブ実装に劣っていると感じる方が居るかも知れませんが、アプリのフレームのみネイティブで実装し、
    その他画面/UIはHTML + CSS + JavaScriptで実装するようなハイブリッドアプリで成功を収めている企業も多数あります。(クックパッドなど)

    実現したいものにより都度試行錯誤、検討が必要だと思いますが、個人的にはネイティブ実装は一部グラフィカルな描画を必要とするもの以外では利用されなくなり、その他のアプリケーションについてはHTML5やJavascript、Dartなどが利用されるようになると考えています。
    何故そう思うのか理由をいくつか並べてみます。

    1. ネイティブの強みであるデバイス機能の操作についても徐々にブラウザ側で対応が施されているため、じきにブラウザからネイティブに遜色なく利用が可能になる(と思う)。

    2. ウェブ向けプログラミング言語の高速化(安定板のSDKの提供が始まっているDartは、V8エンジンよりも高速にレンダリングが可能)によりオーバーヘッドが気にならなくなる。

    3. 通信回線速度の成長(LTE、そして4Gへ)により、サーバーサイドテクノロジーをより発揮できる実装手法/構築方法(ウェブ向けの開発)が選ばれるようになる。

    4. クロスプラットフォーム対策、工期短縮、価格圧縮、保守コスト軽減のため、テクノロジーが統一され始める!デバイス間の統一だけでなく、サーバーサイドとクライアントサイドのテクノロジーも統一される(して欲しい)。
    現状だとNode(JavaScript)、CoffeeScript、Dart、GWT(JAVA)などが選択肢にありますが、個人的にはGoogleの本気度や政治的問題や各々の言語の特性を考えると、1年後くらいにはDartが今以上に使われているのではと予想しております。

    5. Web技術の表現力の拡張(HTML5/CSS3を利用したパララックス効果やWebGLを利用した3D表現など)
    WebGL(OpenGL)についてはAndroid、iPhone(iAd広告のみ何故か対応)ともに一部Android端末以外未対応の状況ですが、近いうちに解禁されると思います。

    6. WebRTCの標準化(現状はChromeとOperaだけだが、Firefoxも実装を進めており、当初反対していたMicroSoftもサポートを計画している、いずれスマートフォンブラウザも!?)

    と色々とWeb贔屓の理由を書いてみましたが、もちろんネイティブが優れている点も多々あります。
    デバイス機能の操作や3Dグラフィカルなどは基本的にはバインディングになりますし、画像の描画においてもブラウザアプリケーション経由のためオーバーヘッドもあります。
    今後も当分は実現したい事、ユーザーに提供したいもの、重要視するもの、予算、工期など様々な条件のなか模索していくことになるでしょう。

    タイトルにも挙げましたが、個人的に特に注目しているのが上記理由の5点目に挙げた「WebGL」と6点目に挙げた「WebRTC」です。
    WebGLは「ブラウザ上で別途プラグインなしに3CGを表現できる」フレームワーク、WebRTCは「ウェブアプリケーション同士が”直接双方向通信”」できるフレームワークです。

    ○WebGL
    WebGLを用いたアプリケーションは今までにも数々登場していますが、まだまだインタラクティブなものは少なく、どちらかというと3次元空間で表現したビジュアルをブラウザでただ単純に見れるといったものが多いです。
    デバイスの対応状況、ソフトウェアの対応状況、技術的な難易度(ライブラリを使わずに全てを把握するとなると正直相当難しいと思います)など理由はいくつか挙げられますが、昨今のデバイスの拡張状況やライブラリの普及を考えると、今後はPlayStationやWiiのようなグラフィカルなゲームもブラウザ上で楽しむのがデファクトスタンダードになるのでは?とも感じています。

    WebGLを利用した参考サイトのURLをいくつか記載します。(ブラウザはChromeでご覧ください)
    http://webglsamples.googlecode.com/hg/aquarium/aquarium.html
    http://alteredqualia.com/three/examples/webgl_animation_skinning_tf2.html

    PlayStationもそう遠くはないですね。
    顔写真だけで高品質な3D顔を生成できるものもあります。
    ヨーロッパのVizagoです。※動画

    すごいですね!

    現在このWebGLの「3D表現の仕組み」を個人的にまったく理解できていないため、基礎から勉強をしようと奮闘中です。
    結果についてはまたどこかで投稿させていただきます。

    ○WebRTC
    直接双方向通信できますので、ブラウザ上で音声やビデオチャットをRTC(リアルタイムコミュニケーション)することが可能になります。
    Cometのように擬似的に同期を表現、且つサーバートリガーで双方向通信する訳ではなく、最近のブログで書いたWebSocketのようにWebアプリケーショントリガーでサーバーと双方向通信する訳でもないです。
    ウェブアプリケーション同士で直接双方向通信できるというのがポイントです。
    Chrome21から標準で使えるようになっており、KinectのようなゲームやブラウザとiOSデバイスの両方でビデオチャットができる世界も登場しています。

    WebRTCについてはWebGLの基礎を習得してから勉強してみます。
    こちらについてもまたどこかで投稿させていただきます。

    ではでは。


  • WebSocketでチャットアプリを作る(with Redis&Node.js) ~続き~

    どうも。営業部の伊藤です。

    先日のブログの続きです。
    Redisの準備が終わりましたので、次はWebSocketを利用するための環境を用意します。

    WebSocketを利用するため、今回はサーバー側のテクノロジーとしてNode.jsを利用する事になしました。
    Jettyなど他にも選択肢はあったのですが、参考文献が多いという理由でnodeにしました。

    では、nodeをインストールします。
    インストールには、nodeを導入するためのシェルスクリプトでインストールを簡略化してくれる「nave」(複数のバージョン管理も可能)やgit上にも様々なオープンソースがありますが、今回は複数バージョンを管理する必要もないため、普通にソースをダウンロードしてコンパイルする事にしました。
    バージョンはインストール当時(2012年9月頃)のstableだった0.8.6にしました。

    # cd /usr/local/src
    # wget http://nodejs.org/dist/v0.8.6/node-v0.8.6.tar.gz
    # tar xf node-v0.8.6.tar.gz
    # cd node-v0.8.6
    # ./configure
    # make install

    次にnode向けのパッケージマネージャであるnpm(Node Package Manager)をインストールします。
    npmで管理されているライブラリは以下URLをご覧ください。
    https://npmjs.org/
    nodeコマンドが利用できるかを確認後、以下を実行します。

    # node -v
    v0.8.6
    # curl http://npmjs.org/install.sh | sh

    対話モードを”yes”と答えていくとインストール完了です。
    これでnodeが利用できる状態であれば、npmコマンドも利用できるようになります。
    早速バージョンだけ確認してみます。

    # npm -v
    1.1.48

    次にパッケージをダウンロードするのですが、その前にnodeの動きを一度確認しておきます。
    王道のHello Worldを表示するため、nodeコンソールを開き以下スクリプトを実行します。

    # node
    > var http = require(“http”);
    > http.createServer(function(req, res){
    > res.writeHead(200, {“Content-Type”: “text/plain”});
    > res.end(“Hello World”);
    > }).listen(3000, “127.0.0.1”);

    ブラウザで上記URL(http://127.0.0.1:3000/)にアクセスすると、画面上に「Hello World」が表示されます。
    単純にテキストを表示するだけなのに、ヘッダーステータスコード200?、listen?
    普段はTomcatやApache(+CGI)といったコンテナ上でサーブレットやPHPでアプリケーションを構築する事が多いと思いますが、nodeの場合はアプリケーションだけでなく、上記サンプルのようにnode自体がコンテナとなります。
    その為、nodeで例外が発生するとサーバ機能ごとプロセスが終了してしまいますので、サーバ機能まで共倒れしないよう例外処理には気を付ける必要があります。
    ※nodeコンソール上で実行していますが、上記スクリプトをファイルに保存し(例えばtest.js)、nodeコマンドのパラメータとして実行する事も可能です。

    ではwebsocketを利用するため、幾つかのパッケージをnpmを利用してインストールします。

    1. Express
    Expressはnode用のWebフレームワークです。
    上述したようにnodeの場合はnode自身がコンテナの役割を果たす必要があるため、静的なファイルの配信についてもnode内で対応しなければなりません。この手間をWebアプリケーションフレームワークであるExpressを利用することで容易に解決できます。
    以下コマンドでパッケージをグローバルインストールします。
    ※グローバルインストールにしないとパスが通らないケースがあるため、以下コマンドでインストールすることをお勧めします。

    $ npm install -g express

    インストールディレクトリにあるapp.jsを実行します。

    $ node app.js

    http://127.0.0.1:3000/ にアクセスして「Welcome to Express」と表示されていればインストール完了です。

    2. Socket.IO
    Socket.IOは今回利用するWebSocketをブラウザの互換性を考慮せずに利用できる抽象化ライブラリです。
    WebSocketを利用できないブラウザではAjaxのロングポーリングを利用するなど、ブラウザ間の互換性を気にせずに開発が可能になります。
    以下コマンドでパッケージをインストールします。

    $ npm install socket.io

    これで一通りの準備が終わりましたので、次はスクリプトを用意します。
    publicディレクトリ内に移動し、chat.htmlファイルを作成します。

    <head>
    <script src=”/socket.io/socket.io.js”></script>
    <script src=”/javascripts/chat.js”></script>
    </head>
    <body>
    <input type=”text” id=”comment” placeholder=”入力してください” value=”” size=20 ></input>
    <button onclick=javascript:send() >送信</button>
    <p id=”list”></p>
    </body>

    次に、javascriptディレクトリに以下ファイル(chat.js)を作成します。
    クライアントサイドのJavaScriptです。

    var postList;
    window.onload=function(){
    postList = document.getElementById(“list”);
    }var listleft = function(){
    postList.innerHTML+=”<div align=’left’><img src=’/images/left.gif’>”+arguments[0]+”</div>”;
    }var listright = function(){
    postList.innerHTML+=”<div align=’right’>”+arguments[0]+”<img src=’/images/right.gif’></div>”;
    }

    var socket = io.connect(“http://127.0.0.1”, {port:3000});
    socket.on(“connect”, function() {
    listleft(“接続開始”); //接続した当人にだけ表示
    socket.emit(“msg send”, “皆さん宜しくお願いします。”);

    // 自分のコメントを表示
    socket.on(“msg mycommnet”, function(commnet){
    listleft(commnet);
    });

    // 他人のコメントを表示
    socket.on(“msg elsecommnet”, function(commnet){
    listright(commnet);
    });

    socket.on(“disconnect”, function(){
    listleft(“接続が切れました。”);
    });
    });

    function send(){
    var comment = document.getElementById(“comment”).value;
    socket.emit(“commnet send”, comment);
    }

    自分が投稿した内容は左側に表示し、自分以外の投稿は右側に表示するようにします。
    最後に先ほど動作確認したapp.jsの下段にsocketインスタンス作成処理を追記します。

    var express = require(“express”);
    var app = module.exports = express.createServer();app.configure(function(){
    app.set(“views”, __dirname + “/views”);
    app.set(“view engine”, “ejs”);
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser());
    app.use(express.session({ secret: “your secret here” }));
    app.use(app.router);
    app.use(express.static(__dirname + “/public”));
    });app.configure(“development”, function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
    });

    app.configure(“production”, function(){
    app.use(express.errorHandler());
    });

    app.get(“/”, function(req, res){
    res.render(“index”, {
    title: “Express”
    });
    });

    app.listen(3000);
    console.log(“Express server listening on port %d in %s mode”, app.address().port, app.settings.env);

    //socketインスタンスを作成
    var io = require(“socket.io”).listen(app);
    io.sockets.on(“connection”, function(socket){
    socket.on(“commnet send”, function (commnet) {
    socket.emit(“msg mycommnet””, commnet); //自分自身に通知
    socket.broadcast.emit(“msg elsecommnet”, commnet); //自分以外で同一ソケットとハンドシェイク中の人にキャスト
    });
    socket.on(“disconnect”, function() {
    log(接続が切れました。”);
    });
    });

    スクリプトの準備が整いましたので、再度サーバーを起動します。

    $ node app.js

    その後、publicディレクトリ内に新規作成したchat.htmlにアクセスします。
    http://127.0.0.1:3000/chat.html

    まず始めに、アイコンがダサ過ぎる点については無視してください。。。
    クライアント側からハンドシェイク要求を送信し、サーバー側からハンドシェイクの応答があれば自身のブラウザだけに「接続開始」というメッセージが表示されます。
    その後、既に同一ソケットにアクセス中のユーザーに接続が確立した旨を伝えるため、「皆さん宜しくお願いします。」とうメッセージを発信(emit)します。
    上記画像の場合、左側のブラウザが先にアクセスしたブラウザで、右側のブラウザが後にアクセスしたブラウザです。
    右側のブラウザで接続後にコメントを送信すると、リアルアイムに左側のブラウザに投稿内容が反映されました。いい感じ♪
    例外処理、DBを利用した機能(過去データ取得など)などは未実装の状態ですが、一旦これでチャットWebアプリの基盤はできました。
    次はRedisを利用した過去データ取得、Publish/Subscribe型(Pub/Sub型)とnodeを連携した冗長化を進めていきます。
    こちらについては進展があればまたブログに投稿させていただきます。

    それでは。


  • WebSocketでチャットアプリを作る with Redis&Node.js

    どうも。営業部の伊藤です。

    もともとHTML5の仕様の一部として策定が進められていたWebSocketについて勉強するため、Node.jsを利用して簡単なチャットアプリを作ってみました。(目指せブラウザ版LINE!)

    今回はデータベースとしてRedisを採用しましたので、まずはRedisのインストールとコマンドラインを使った簡単な動作確認までを書きます。

    まず、Redisをインストールします。
    yumでもインストールはできますが、最新をインストールするため、ソースからインストールします。
    ※2012年9月頃

    # wget http://redis.googlecode.com/files/redis-2.4.16.tar.gz
    # tar zxvf redis-2.4.16.tar.gz
    # cd redis-2.4.16
    # make
    # make install

    あっという間です。
    RedisはANSI-Cで実装されているため、インストール時に依存ライブラリなどのインストールが不要です。
    よって、コンパイルは上記ステップで完了となります。

    既知の情報だと思いますが、念のため。
    Redisはインメモリベースの
    KVSです。
    KVSはRDB(リレーショナルデータベース)のJOINのように複雑、且つ高負荷なSQLは発行せず、高速化/拡張性に趣をおいたNoSQLのデータベース管理システムとなります。
    いわゆる分散型データ保管技術の1つです。
    同様のデータベースにはGoogle検索で採用されているBigtable(ビッグテーブル)、Amazonで採用されているDynamo(ダイナモ)、有名どころだとmemcachedなど様々な種類があります。
    また、RDBのような複雑なシステムではないため、サービス(mixi、Amazonなど)や大学の研究で独自のKVSが開発/採用されているケースも多々あります。

    そんなKVSですが、KVSごとの特徴は?ということで、今回選定したRedisの特徴をいくつか記載します。
    1. 永続化機構
    通常メモリ上でデータを管理しているため、memcachedのように障害時、再起動時にはデータを喪失してしまいますが、Redisは定期的にデータのスナップショットをダンプファイルにし、再起動時にこのファイルをメモリに読み込むことでデータを復元する仕組みを備えています。
    定期的という事で不安を抱く方も多いと思いますが、”Append Only File”という仕組みを利用することで全ての更新コマンドを管理しておき、再起動時にこのコマンドを読み込むという方法もあります。これであればデータの喪失は最小限に食い止めれます。
    注意すべきなのは、永続化機構といってもあくまでもインメモリベースのため、最大容量はRAMに依存します。
    RAM容量より多くのデータを保存したい場合はスワップファイルを利用し、一部のデータをHDDに退避させる必要があります。(RedisVM)

    2. 豊富なデータ型とコマンド
    Redisはハッシュ(HASH)以外の型もサポートしており、リスト型(LIST)、集合型(SET)、順序付き集合型(SORTED SET)などのデータ構造が扱えます。
    また、コマンドが非常に豊富、且つアトミックに操作が可能なため、CAS操作などトランザクションを考慮した処理が不要です。
    ただし注意すべき点として、アトミック実行(MULTI/EXEC)は一般的なトランザクションとして実行されている訳ではないため、実行中にエラーが発生した場合にROOLLBACKはされません。残りの処理も実行されます…ここは要注意です!
    (値を設定&取得くらいのアトミック操作であれば”GETSET”という1つのコマンドで対応可能)
    KVSの利用用途としてはデータの整合性よりもスピード重視の利用用途が多いと思いますので、トランザクションを考慮する機会は少ないと思いますが、アトミックに操作が可能な事でデータ管理方法の幅が広がります。

    3. レプリケーション
    Redisはマスター/スレーブのレプリケーションが設定ができ、スケールアウトが非常に容易です。
    設定は簡単で、スレーブ側の設定ファイルに以下項目を追加するだけです。

    # cp redis.conf redis_slave.conf
    # vi redis_slave.conf

    # port 7000 <= スレーブはポート7000番で稼働
    # slaveof localhost 6379 <= slaveofにマスターのホスト名とポート番号を指定

    # redis-server redis_slave.conf

    設定できているか否かの確認はinfoコマンドを実行するか、実際に値を設定して確認します。
    infoコマンドの場合、実行後に「role」を確認してください。

    # redis-cli -p 6379 -h localhost <= マスターに接続
    redis 127.0.0.1:6379>info

    role:master

    役割がマスターになっていますね。次にスレーブに接続します。

    # redis-cli -p 7000 -h localhost <= スレーブに接続
    redis 127.0.0.1:7000>info

    role:slave

    役割がスレーブになっていますね。無事設定できているようです。

    最後に、実際にレプリケーションできているかを確認するため、マスターにデータを登録し、その後スレーブでデータを取得してみます。

    # redis-cli -p 6379 -h localhost
    redis localhost:6379> set test “test-data”
    OK
    redis localhost:6379> get test
    “test-data”
    redis localhost:6379>exit# redis-cli -p 7000 -h localhost
    redis localhost:7000> get test
    “test-data”
    redis localhost:7000>

    問題なさそうです。

    4. Publish/Subscribe型(Pub/Sub型)の通信をサポート
    今回DBにRedisを採用したのはこの「Pub/Sub型通信」をRedisがサポートしているためです。
    送信側と受信側がトピックを介して”多対多”で通信できるモデルのため、Redisを中継サーバとしてWebSocketサーバを冗長化する事ができます。
    Node.jsについて説明できていないため順番が前後しますが、Nodeを利用してPub/Sub通信の動きを確認してみます。

    nodeのコンソールを起動し、以下スクリプトを実行します。

    # node
    > var sys = require(“sys”);
    > var redis = require(“redis”);> var subscriber = redis.createClient(6379, “localhost”);
    > subscriber.subscribe(“I am subscriber”);
    > subscriber.on(“message”, function(channel, message){
    > sys.puts(channel + “に「” + message + “」というメッセージが送信されたようです。”);
    > });

    > var publisher = redis.createClient(6379, “localhost”); //publisherは敢えて作る必要ないです。var subscriberを使って問題無いですが、分かりやすくするため!
    > publisher.publish(“I am subscriber”, “I am publisher”);

    6379番ポートのredisの「I am subscriber」というトピックに対して配信を申し込み(subscribe)、配信があればコンソール上に受信内容を出力するようにします。
    その後、6379番ポートのredisの「I am subscriber」というトピックに対して”I am publisher”というメッセージを配信します。

    上記スクリプトを実行すると、以下メッセージが表示されます。

    > I am subscriberに「I am publisher」というメッセージが送信されたようです。

    Pub/Sub通信のイメージ湧きましたでしょうか?
    分かり辛いですね…なんというか…自作自演ってな感じですね。

    という事で、上記コンソールはそのままにしておき、別コンソールを起動して以下コマンドを実行してみます。

    # redis-cli -p 6379 -h localhost
    redis localhost:6379> publish “I am subscriber” “I am publisher”

    するとnodeコンソール上に先ほどと同様に以下メッセージが表示されます。

    > I am subscriberに「I am publisher」というメッセージが送信されたようです。

    これこそがPub/Sub型の配信モデルです。
    node側ではトピック「I am subscriber」を購読(subscribe)状態のため、
    別イベントで「I am subscriber」にメッセージが登録されたと同時にメッセージをお知らせしてもらっています。

    今回は受信/購読者(Subscriber)、発行/公表者(Publisher)共に単一で確認しましたが、前述したように多対多の通信モデルのため、複数のSubscriber、またはPublisherでも上記通信が可能となります。
    WebSocketサーバーを冗長化した際に各々のサーバーでsubscribeしておき、連携したいデータをpublishすればサーバー間連系はこの中継サーバーに委ねられます。
    単一であればWebSocketでブロードキャストすればいいだけですが、冗長化を考えるとPub/Sub型を利用できるredisは非常に便利です。
    エンドポイントはWebSocketで管理しているため、サーバー間はPub/Sub型じゃなくてTriggerでもいいんじゃない?という方も居ると思いますが、サービス独自の特殊な通信制御が必要でない限りはPub/Sub型通信モデルの方が便利だと思います。

    Redisの準備は一旦ここまでとし、次はNodeの準備を進めます。
    少し長くなってしまったため、Nodeと各種ライブラリ(Socket.IOやExpressなど)のインストールについては
    次回ブログで投稿させていただきます。


  • 東京スカイツリーに行ってきました!

    7月初旬に当日券が解禁されたということもあり、
    先週末スカイツリーに登ってきました。

    東京タワーと同じで展望台は2段階あり、
    1段階目の「天望デッキ」は350メートル(この時点で東京タワーより高い!!)、
    2段階目の「天望回廊」は450メートルにあります。
    「天望デッキ」までが2000円、「天望回廊」まで登るには更に1000円。
    遊園地のように長居する事はなく客の回転は早いため、恐らく相当稼いでます!はい。

    お金の話は置いておいて…
    登ってみた感想を書きます。

    まずはエレベーターに乗るまでのお話しです。
    ネットで購入すると待ち時間もないということで空き状況を確認してみたのですが、土曜日は11月末まで埋まっていました。
    (日曜日であれば11月から空いていました。)
    待つのが面倒なため、ここで当日券で行く事を決めました。

    いざ出発!

    2時間くらい並ぶんだろうな~っと腹を括りチケット売り場に向かったところ、
    想定外の待ち時間!!
    なんとたったの1時間です。想定より短いですよね?
    実際あっという間でした。

    チケットを購入し、直ぐにエレベーター乗り場に。

    エレベーターは全部で4基あり、それぞれに季節(春夏秋冬)を彩った装飾が施されていました。
    私は登りが「夏」で下りが「春」でした。
    非常に綺麗なモチーフが描かれており、こういうところが日本の精細さ、美的感覚なんだな~と感動したのを覚えています。

    エレベーターの搭乗時間ですが、感覚的に20~30秒くらいだったと思います。(勘)
    よくある超高層ビルの乗り心地の悪さは無く、エレベーター内のアートを見ていたらあっという間に350メートルでした。

    到着!!

    エレベーターのドアが開くと、そこには一面に広がる絶景がありました。
    日中に行ったため夜景のようなロマンチックな雰囲気はありませんでしたが、
    非現実的な高所に圧倒されたのを覚えています。
    あそこまで高いと逆に恐怖感は無くなりますね。

     

     

     

     

     

     

    次は「天望回廊」へ。

    混雑しているようであれば「天望回廊」は諦めようと考えていたのですが、たった”5分”の待ち時間で450メートルまで行けるという事で、更に上昇する事に。
    (「天望回廊」へのエレベーターは特に何の仕掛けもない普通のエレベーターでした。)

    到着!!

    エレベーターのドアが開き、期待を胸に一歩踏み出しました!!

    残念ながら…というか私が繊細じゃないの原因だと思いますが、
    350メートルも450メートルも高すぎるので、あまり「天望デッキ」
    との違いは分かりませんでした。。。

    回廊を進むと、途中で最高地点(451.2メートル)があります。
    日本の建物で一番高い所にいるぞーっ!と少しだけ興奮できると思います。是非。

     

     

     

     

     

     

     

    以上です!

    あまりスカイツリーの良さは伝えきれませんでしたが、、、
    拍子抜けする事はないと思いますので、お時間があれば是非足を運んでみてください。
    スカイツリーに連結している東京ソラマチ(Solamachi)には飲食店、服屋、雑貨屋など多数の商業施設があり、ここだけでも十分に楽しめます。

    今回は友人と登ったため、次は嫁さんと夜景でも見に行こうと思います。

    では。


  • ハーフマラソン走ってきます!

    遂に、今年の目標であるハーフマラソンに申し込みました。

    東京マラソンのようにメジャーではないですが、全国ランニング大会100選
    にも選ばれている「成田POPラン大会」です。
    http://www.city.narita.chiba.jp/sisei/sosiki/shosport/std0048.html

    この「POP」というキーワードは想像通り「POPULAR」の頭文字で、
    誰でも気軽に参加できる大会という意味だそうです。
    ただ、、、色々と調べ続けるとアップダウンが激しく、それなりに
    厳しいコースという評価が多かったため、舐めてはかかれません。
    まだ一度に12キロ以上走った事がないため、既にビビり倒してはいますが。。。
    あと2ヶ月ありますが、余裕を持って準備しておきます。

    目標は「1度も歩かず完走し、2時間をきる!」です。

    11月半ばに完走結果をご報告できるよう頑張ります。