VOYAGE GROUP エンジニアブログ

voyagegroup_techのブログ
VOYAGE GROUPエンジニアブログです。

Socket.IO サーバーを Haskell で書いて Yo っぽいものを作ろう

こんにちは。adingo で広告配信のお手伝いをしている井手上です。
今回のエンジニアブログは Haskell の Socket.IO サーバーライブラリを使って、Yo みたいなアプリケーションを作成した話です。

前置き

突然ですが、皆さんはお仕事や個人的な開発ではどんなプログラミング言語・ミドルウェア・プラットフォームを利用していますか?
 
Web アプリケーションを作るなら Python や Ruby 等のいわゆる Lightweight Language、大量のリクエストを高速に、並列に処理したいのなら Erlang など、目的に応じた選択肢があると思います。色々な選択肢がある中、以前は最適だと思って選択したものが、年々新しい技術がが出てきているために今ならもっと良い選択ができるという状況になることもあるかと思います。

私が働いているチームでは採用ページの「業務内容」に書いてあるとおり「合理的な理由がありチームにとって受け入れ可能であれば別な技術の採用や別な技術へのリプレースを拒む理由はありません」ということで、新しい技術での開発やリプレースがよく行われています。最近ですと、Perl で書かれていた広告配信システムの一部機能を自由に高速化するコンペを行い、優勝したシステムを採用するという取り組みを行い Golang 製のシステムが優勝しました。自分も最近始めた Haskell をどこかに投入してやろうと虎視眈々と狙っているところです。

その Haskell ですが、実際に投入するかどうかはともかく、関数型プログラミングや型システムといった多くのプログラミングパラダイムを学べるお得な言語です。自分が知らないパラダイムを学ぶことは問題の捉え方や問題解決のための選択肢を増やすという点で役立ちます。今回は、Socket.IO 1.0 サーバーの Haskell 実装が出てきたということで使ってみました(前置きが長い)。

Socket.IO 1.0


Socket.IO はクライアントとサーバーとのリアルタイム通信を実現するフレームワークです。チャットアプリケーションやドキュメントの共同編集機能のように、リアルタイムなやりとりに利用されます。クライアントには主にブラウザが挙げられ、通信プロトコルには WebSocket を利用しています。Socket.IO は 2010 年からあるようですが、2014 年 5 月に Socket.IO 1.0 となりました(GitHub リポジトリのコミット履歴より)。

個人的に Socket.IO 1.0 で面白いと感じている点は、大きく分けてモジュールが Socket.IO と Engine.IO に分かれている点です。Engine.IO はトランスポート層のプロトコルの違いを吸収することに責任を持ちます。Socket.IO には Engine.IO よりもアプリケーション寄りの高レベルな機能が実装されており、自動再接続や名前空間機能を提供しています。

Engine.IO でのトランスポート層プロトコルの違いを吸収する方法について簡単に説明しますと、接続開始時は接続できる可能性が高いロングポーリング方式で通信を開始し、WebSocket での通信が可能ならば、WebSocket に切り替えるというように、upgrade していく方式でトランスポートの違いを吸収しています。WebSocket 接続のタイムアウトを待ってロングポーリングに切り替えるというような fallback 方式ですと、最初はつながらないので、WebSocket を使用できないユーザーにとっては接続までが長くてイライラしてしまうかもしれません。upgrade 方式だとはじめから接続できるので、ユーザー体験は fallback 方式よりも悪くないことが期待できます。
 
ocharles/engine.io

Socket.IO というと、公式の提供しているサーバーが Node.js 製なので、サーバーも JavaScript で書くイメージがありますが、プロトコルさえ実装されていれば良いので、サーバーは Node.js でなくても構いません。今回使用する ocharles/engine.IO は Socket.IO 1.0 を Haskell で実装したサーバーになります。

Node.js よりも Haskell でサーバーを書くことの良い点は、期待しているデータの型を定義しておけば JSON のパース時にデータのチェックを行う必要がないというところかなと思います。

下記の例は addPerson というメッセージがきたときに、data に name と age というプロパティが存在するかチェックしている例です(JavaScript版)。age は数値を期待されているはずなので、数値であるかどうかのチェックまで必要かなと思ったり心配事は多いです。 


次は同じ例の Haskell 版です。addPerson メッセージがきたときに JSON データを Person データ型に変換する処理が入ります。その時に name や age が存在するか?フィールドの型として正しいか?というようなことがチェックされます。データ型を定義しておけばその定義に沿わない JSON はパースできないので、パースとデータのチェックが同時にできます。


上記の例を比較してみると、JavaScript 版の方がコード量は少なくカジュアルに書けそうですね。アプリケーションが行う通信でのインターフェースをしっかり決めて、それが複雑であったり、データのチェック項目が多い等の場合は Haskell でサーバーを書くのもありかもしれません。

KaitenZushi


今回は Socket.IO を使ったアプリとして、KaitenZushi という Web アプリケーションを作ってみました。これは最近話題になった Yo というアプリを意識しています。Yo は誰かに「Yo」というメッセージを送るだけのシンプルなアプリです。例えば朝方に Yo と言われると、(たぶん)おはようの意味だったりと、人同士の関係や時間等々の様々なコンテキストを考えて、Yo を受け取った側が何となく解釈するという大変ハイコンテキストなアプリです(自分の理解では、ですが)。

KaitenZushi はもう少しコンテキストを絞ったらどうなるのかな?という考えで作りました。右上の入力ボックスからコンテキスト(コンテキスト = 寿司ネタをイメージしてます)を作成して、寿司(コンテキスト)を、同じコンテキストにいる人たちにブロードキャストできます。なかなか用途がわからないアプリだと思いますが、自由に寿司を配ってみてください。

KaitenZushi のリポジトリは以下になります。サーバー側のコードはとても短く、寿司がサーバーに emit されたら、接続している人全員に寿司を broadcast するという機能だけです。


ちなみに今回は Node.js で Haskell 版と同じ機能を実装して heroku 上にデプロイしています。ocharles/engine.io は Snap や Yesod といった Haskell 製の Web アプリケーションフレームワークにも対応しており、heroku もそれらに対応しているのですが、今回は時間の都合上、サクッとデプロイできた方を使用しました。

まとめ

Haskell の勉強ついでに KaitenZushi という Socket.IO 1.0 を利用した Web アプリケーションを作ってみました。まだまだ道半ばですが、自分の知らないプログラミングのパラダイムを学ぶことは楽しいです。

最後に、弊社には ajito という社内バーがあり、エンジニアが夜な夜な出現してビールを飲んだり技術的な話をしたりと楽しい場になっています(#ajiting といいます)。弊社エンジニアに一声掛けてくださると、無料でビールを飲みつつ歓談できるかと思います。また、勉強会の場としても提供していたりするので、どんどんお声掛けください!そして、KaitenZushi にも ajiting のコンテキストを作ったので、いらっしゃった時には使ってみてください!

参考

Web Notification をサービスに実装するにはどうしたらいいか妄想してみた

こんにちは。 VOYAGE GROUP でエンジニアをしています tatenosystem です。
 
最近、社内で slack というサービスを良く使っているのです。 
しかし、結構書き込みを見逃しがちで 「定期的に確認するのはめんどくさいなぁ」 と思ったら、なんとデスクトップ上に通知をしてくれる 「Web Notification」 に対応しているじゃないですか。これは便利!

WEBページからの通知があると、PC画面上に 「通知ウィンドウ」 が出てきます。
こんな感じ。

slacknotice
 
しかもブラウザの機能で行っているので、特別なプログラムはいらないです。
まぁ、対応しているブラウザでないとダメなんですが。。。

かなり前(たしか2010年くらい?)から 「Web Notification」 の名前は聞いたことはありましたが、本格的にサービスとして使っているサイトは始めてみました。さすが slack。

誰も使わずに滅んだと見せかけて、急に使われだすというのは 「Bluetooth規格」 を思い出します。

そんな訳で個人的に盛り上がったので、WEBサービスに 「Web Notification」 を実装するにはどうしたらいいか妄想してみました。

調べたところ、勝手に通知がポコポコ上がると鬱陶しいので、ドメインごとに「許可」する必要があるみたいです。
「Web Notification」 の状態(Permission)は以下の3つ。

<Web Notification Permissions>
  • default … ユーザーの承認を取っていない状態
  • granted … 許可された状態
  • denied … 拒否された状態

では、最初に 「Permission を取得できる関数」 を JavaScript で作ります。
非対応ブラウザも状態として取得したかったので、勝手に「unusable」という状態を追加した関数を作成しました。

function noticPermission()
{
  if (! window.Notification) return "unusable";
  if (Notification.permission) {
    return Notification.permission;
  } else {
    return ((new Notification("check")).permission);
  }
}

次に HTML ですが、Permission 状態で表示する内容が違うので状態ごと4種類の表示内容を記載します。
HTML DOM の id は「状態+"WebNotice"」としました。初期状態はすべて非表示です(style="display:none")

<p>[設定] </p>
<p>
<div id="unusableWebNotice" class="infoWebNotice" style="display:none">
    Your browser does not support desktop notifications.
</div>
<div id="defaultWebNotice" class="infoWebNotice" style="display:none">
    <button id="requestPermission">Enable desktop notifications</button>
</div>
<div id="grantedWebNotice" class="infoWebNotice" style="display:none">
    <input id="canWebNotice" type="checkbox"><span>Desktop Notifications</span>&nbsp;<a href="#" id="showWebNoticeTest">Send test notification</a>
</div>
<div id="deniedWebNotice" class="infoWebNotice" style="display:none">
    Desktop Notifications are currently disabled.
</div>
</p>
Permission 状態ごとの表示内容はこんな感じ。

  • unusable … 非対応ブラウザだよーというメッセージを出します
  • default … 「承認ボタン」を表示します
  • granted … Web Notification(デスクトップ通知)を使用するかどうかのチェックボックスを表示
  • denied … Web Notification(デスクトップ通知)が拒否されてるよーというメッセージを出します
granted の場合はテストリンクも表示するようにします。
完全に slack の パク リスペクト。

すべてを表示すると、こんな表示になります。

confightml01

Permission に合わせた HTML を表示 しなくてはなりません。
というわけで関数を作成します。
HTML DOM の id を「状態+"WebNotice"」としたので、簡単に DOM 指定ができて便利です。

function showInfoWebNotice()
{
  $(".infoWebNotice").hide();
  $("#"+noticPermission()+"WebNotice").show();
}

で、ページ表示のタイミングでこの関数を実行するわけですが、私が好きな jQuery を使います。

$(function(){
  showInfoWebNotice();
});

つぎに 「承認ボタン」を押された時 の事を考えます。
 「Web Notification」 の Notification.requestPermission() を使用してユーザーに承認要求を行います。
「承認ボタン」のクリックイベントに Notification.requestPermission() を設定します。
コールバックが指定できるので、コールバックで先ほど作った showInfoWebNotice() を呼び出し、「承認」もしくは「拒否」された直後、画面表示を切り替えます。

$(function(){
  showInfoWebNotice()

  $('#requestPermission').click(function(){
      Notification.requestPermission(function(){
        showInfoWebNotice();
      });
  });
});

いよいよ本命の Web Notification を行う関数を作ります。
slack の Web Notification は数秒で自動で消えますので、これも真似しましょう。
setTimeout を利用して一定時間後に自動で通知ウィンドが消えるようにします。
「チェックボック」を確認して、チェックされていなかったら処理を行わないようにします。 

この関数には、「タイトル、本文、アイコン、表示時間(ms)、テストの有無」 を引数で渡せるようにします。

表示時間(ms) は「0」指定で無制限。
テストモードの場合は「チェックボック」にチェックがなくても「通知」できるようにします。

function doWebNotification(title, body, icon, timer, test)
{
    if(typeof timer === "undefined") timer = 0;
    if(typeof icon === "undefined") icon = "";
    if(typeof test === "undefined") test = false;
    
    if (! test) {
        if (! $("#canWebNotice").is(':checked')) return;
        if (noticPermission() != "granted") return;
    }
    
    var notification;
    notification = new Notification(title, {body: body, iconUrl: icon, icon: icon});
    notification.onclick = function() {
        notification.close();
    };
    
    if (timer != 0) {
        setTimeout(function(){
            notification.close();
        }, timer);
    }
}

これで Web Notification を使う準備ができました。
さて、いよいよ doWebNotification() を使ってみましょう。

チェックボックスの右にある 「Send test notification」 をクリックした時、テストモードで doWebNotification() を呼び出すようにします。1.7秒後に自動で通知が消えるようにします。 

$(function(){
  
  ………
  
  $("#showWebNoticeTest").click(function(){
      doWebNotification("Hello", "Hello WebNotification", "", 1700, true);
      return false;
  });
});

テストモード以外の通知ボタンも作りましょう。ボタンを押したら Web Notification を行うようにします。
HTMLにボタンを配置して、

<button id="showWebNotice1">SHOW NOTICE 1</button>
<button id="showWebNotice2">SHOW NOTICE 2</button>
<button id="showWebNotice3">SHOW NOTICE 3</button>
<button id="showWebNotice4">SHOW NOTICE 4</button>

イベントを設定します。

$(function(){
  ……
  
  $("#showWebNotice1").click(function(){
      doWebNotification("テスト", "ヘ(^o^)ヘ\n  |∧\n  /", "testicon.png", 1500);
  });
  $("#showWebNotice2").click(function(){
      doWebNotification("テスト", "  (^o^)/\n /(  )\n/ >", "testicon.png", 1500);
  });
  $("#showWebNotice3").click(function(){
      doWebNotification("テスト", " (^o^) 三\n (\\ 三\n< \ 三", "testicon.png", 1500);
  });
  $("#showWebNotice4").click(function(){
      doWebNotification("テスト", " \\n (/o^)\n ( /\n / く", "testicon.png", 1500);
  });
});

これで、チェックボックにチェックを入れ、ボタンを押すとPC画面上に「通知」が行われます。

webnotice01

↓今回のプログラムは下記ページで実行できます。

なお、今回作成した JavaScript は「Windows Chrome v36.0.1985.125m」でしか動作確認していません。ご了承ください。多分対応しているブラウザなら動くとは思います。
次回は 「Web Notification」 を使って、なにかサービスを作って見たいたいですね。

SQLアンチパターンのススメ

こんにちはシステム本部 三浦@hironomiuです。

VOYAGE GROUPでは様々な社内勉強会を開催しているのですが今年の1月からoreillyから出版されているSQLアンチパターン勉強会を三浦@hironomiuが開催していました。

本の内容は名前の通りSQL(RDBMSまで広げた)を扱う際の「べからず」集です。なかなかの良書なのでまだお読みでない方は一読をおススメします!

勉強会の進め方ですが
  • 週1時間
  • 1回あたり2章~3章のペースで進める
  • ベテランと若手を同じぐらいの人数(合計で最大8人)にて進める
  • 輪読ではなく三浦@hironomiuがメンターとし進め、適時参加者が経験談を語る形で進める
のような形で進めました。本来ですと1章1週でも良い内容なのですが全25章、25週はモチベーションを維持することに不安があったのでこのように進めました。

全25章となかなかのボリュームでしたが勉強会は第2期まで開催し、どちらも無事最終章まで11週間で終えることができました。(77日!!改めて見ると長いですね!)

開催中、章によってはベテランから「あるある!」と自身の経験などの話なども起こり若手にも有意義な知見が伝わったと思っています。
章ごとに様々な知見が得られるだけでなく例えば1章ではジェイウォークの命名について解決策である連関エンティティを交差テーブル(交差点)に見立てて名付けているあたりに深みを感じられたりします。

11週続ける中では開催中、監訳者のお一人、和田卓人@t-wadaさんにもお越し頂きアドバイスを頂ける週もあり全体を通して大変有意義な勉強会になったと思っています。

全11週にて各週で勉強会の際に用意した資料は下記となります。


今後も第3期、第4期と開催し内容、密度、資料もブラッシュアップしていきたいと思います!!
記事検索
QRコード
QRコード