エンジニアカフェ




PlayFramework + WebSocket 勉強会
PlayFramework1.2.4におけるWebSocket
             原 一浩   @kara_d
原 一浩   @kara_d   $	
  play	
  netbeansify
何してる人?




         Greative is Great Creative
トレンド 、統計、デザイン、システム
   デザイントレンドリサーチをベースに、デザインされたアプリ
   PlayFramework、R言語、CakePHP、Flexなど受託制作
4/4のJavaOneに参戦!!




 JavaOneでスピーカーします
 ➡   PlayFramework + WebSocketでつくる
     リアルタイムWebアプリケーション




       Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   4
アジェンダ



 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   5
本日のゴールと目次

本日のゴール
➡   なんとなくWebSocketアプリケーションが作れるくらい
➡   PlayFrameworkにおけるWebSocketControllerの全体理解

目次
➡   フロントエンドから見た、リアルタイム通信サーバのこれまで
➡   WebSocketについて
➡   WebSocket未対応ブラウザへの対応
➡   PlayFrameworkとWebSocket
➡   Sample chatでみる、WebSocketアプリの構成
➡   WebSocketController
➡   F.javaの話
➡   WebSocketアプリケーションの設計・構築手順
       Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   6
フロントエンドから見た、
リアルタイム通信サーバの
これまで


 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   7
昔々のリアルタイム通信コンテンツの定番

Flashでのリアルタイム通信
➡   Adobe Flash Communication Server
➡   Adobe Flash Media Server

Real Time Messaging Protocol(RTMP)
➡   Red5
    •   http://www.red5.org/

    •   Javaで作られている

    •   オープンソース




         Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   8
WebSocketについて



 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   9
WebSocketの仕様について

 WebSocketは、W3CとIETFが策定している
 ➡   元々はHTML5の仕様の一部
 ➡   双方向の通信技術
 ➡   AjaxやCommetよりもプッシュ技術の仕組みとして自然
 ➡   一度コネクションを行った後は専用の通信プロトコルで通信
     •   ハンドシェイク

     •   ws:もしくはwss:
 ➡   最終仕様は、RFC6455。対応は下記(※は未調査)
     •   IE 10 Platform Preview 5※

     •   Firefox 11

     •   Google Chrome 16

     •   Safari 最新※


          Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   10
WebSocket未対応ブラウザへ



 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   11
クロスブラウザ対応の力強い味方

web-socket-js
➡   https://github.com/gimite/web-socket-js
    •   Google Chrome 4 or later, Firefox 6 or later (uses native
        WebSocket or MozWebSocket implementation)

    •   Firefox 3 to 5, Internet Explorer 8, 9 + Flash Player 10 or later




         Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   12
web-socket-jsのインストール方法(1)

 PlayFrameworkでの利用
 ➡   ダウンロードしたら、
     •   public/javascripts/ に「swfobject.js」「web_socket.js」を配置

     •   public/swfs/ に「WebSocketMain.swf」を配置
 ➡   main.html(レイアウト)に記述

     <script	
  src="@{'/public/javascripts/swfobject.js'}"	
  
     type="text/javascript"	
  charset="$
     {_response_encoding}"></script>
     <script	
  src="@{'/public/javascripts/web_socket.js'}"	
  
     type="text/javascript"	
  charset="$
     {_response_encoding}"></script>




          Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   13
web-socket-jsのインストール方法(2)

 JavaScriptでのWebSocketの接続方法
     WEB_SOCKET_SWF_LOCATION	
  =	
  
     	
  	
  	
  	
  "@{'/public/swfs/WebSocketMain.swf'}";
     WEB_SOCKET_DEBUG	
  =	
  false;

     var	
  socket;
     socket	
  =	
  new	
  WebSocket(
     	
  	
  	
  	
  '@@{WebSocket.ChatRoomSocket.join(user)}')


 ➡   Flashを介している点がデフォルトの方法と異なる
 ➡   こうすることでFirefoxなど未対応ブラウザでも動作可能




        Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   14
PlayFrameworkと
WebSocket



  Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   15
PlayFrameworkとWebSocket戦いの歴史

 Play1.2.3                 Play勉強会

 ➡   hybi-07
 ➡   version 7(だったような...)
 ➡   Chrome 14までサポート

 Play1.2.4
 ➡   hybi-10
 ➡   バージョン8
 ➡   Chrome 16までサポート

 パッチ(Lighthouse 1240 patch)                                                                   Play合宿
                                                                                         Play, WebSocket and CakePHP
                                                                                         mini hack-a-thon 雪山合宿
 ➡   RFC6455
 ➡   最新版に対応

        Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)                       16
パッチを当てれば動く!!

パッチ(Lighthouse 1240 patch)の当て方
 ➡   https://github.com/playframework/play/pull/438 を参照
 ➡   framework/src/play/server/PlayHandler.java を修正
 ➡   修正後、antタスクを実行

     $	
  ~/play-­‐1.2.4/framework
     $	
  ant	
  
     Buildfile:	
  /Users/harakazuhiro/play-­‐1.2.4/framework/
     build.xml
     .....
     BUILD	
  SUCCESSFUL
     Total	
  time:	
  22	
  seconds




        Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   17
パッチがあたると、こうなる

パッチが当たったリビルド版
➡   バージョン表記が「play! 1.2.x-localbuild」に

    $	
  play	
  run	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
                                                                                                                                                                                                      	
  
    ~	
  	
  	
  	
  	
  	
  	
  	
  _	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _	
  
    ~	
  	
  _	
  __	
  |	
  |	
  __	
  _	
  _	
  	
  _|	
  |
    ~	
  |	
  '_	
  |	
  |/	
  _'	
  |	
  ||	
  |_|
    ~	
  |	
  	
  __/|_|____|__	
  (_)
    ~	
  |_|	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  |__/	
  	
  	
  
    ~
    ~	
  play!	
  1.2.x-­‐localbuild,	
  http://www.playframework.org




              Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)                                                                                                       18
パッチについてのご意見

パッチ(Lighthouse 1240 patch)に関する情報




    @gtk2k さんより
    @kara_d versionにはクライアントのSec-WebSocketフィールドの値 は、 
    versionにはクライアントのSec-WebSocket-Versionフィールドの値 の間違い
    です。



    res.setHeader("Sec-WebSocket-Version", "13");
➡

    が正解?

       Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   19
PlayFramework以外の状況

 Play以外は追いかけれていませんが、ご参考まで




   •




       Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   20
MVCとポートの話

WebSocketをMVCフレームワークに載せる意味
➡   Webアプリケーションとのシームレスな統合
➡   MVCフレームワークでWebSocketもコントローラーとして
    ラッピングすることで、設計が容易に
➡   認証系、データ管理系の実装が楽になる
 • 負荷を考えると結局は別サーバー?
WebSocketアプリケーションとポート
➡   基本的には、Playが動いているポートになる
➡   Apacheをフロントに置いて、mod_proxyを経由させている
    場合は注意




      Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   21
接続先は自動で生成可能

PlayFrameworkとJavaScriptとの連携
➡   WebSocketのポート、アドレスの出力

    socket	
  =	
  
    	
  	
  	
  	
  new	
  WebSocket(
    	
  	
  	
  	
  	
  	
  	
  	
  '@@{WebSocket.ChatRoomSocket.join(user)}'
    	
  	
  	
  	
  )


    とすると、
    socket	
  =	
  
    	
  	
  	
  	
  new	
  WebSocket(
    	
  	
  	
  	
  	
  	
  	
  	
  'ws://localhost:9000/websocket/room/socket?
    user=hara'
    	
  	
  	
  	
  )

    と出力される。
    •   @@{}で絶対URLになる

        Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   22
Sample chatでみる、
WebSocketアプリの構成



 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   23
付属のサンプル話

PlayFrameworkでのWebSocketのサンプル
➡   samples-and-tests/chat
    •   Reflesh形式

    •   Commet形式

    •   WebSocket形式




         Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   24
WebSocketアプリケーションの基本モデル

 WebSocketアプリ:サーバーとクライアント

         DB              PlayFramework                            WebSocketハンドシェイク



                                                                                                   Webサイト
                           Inbound                                           WebSocketソケット通信

           WebSocket
                                              Outbound
           Controller

アプリごと
にイベント           Model                WebSocket
デザインが           Event                  Event
必要
                  Join                TextFrame



              Message                SocketClose



                 Leave              BinaryFrame




        Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)        25
サンプルのチャットアプリケーションを読み解く(1)

全体構成
➡   クライアント
    •   コマンドを「:」でつないだシンプルなメッセージ方式

    •   コマンド : ユーザー : メッセージ

    •   message : name : text
➡   サーバー
    •   WebSocketコントローラー内にChatRoomSocketという
        WebSocketControllerクラスを継承したクラスがある

    •   ChatRoomSocketには、joinメソッドのみがある

    •   ChatRoomモデル内でチャットのイベントを設定

    •   roomMessagesStreamイベントストリームを作成

    •   inbound.isOpen()の限りループ
        -   ただし、Either<WebSocketEvent,ChatRoom.Event> eに値が入らない限りは停止


            Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   26
サンプルのチャットアプリケーションを読み解く(2)

  •     各種イベントにマッチするものがあれば、各処理を実行
       -      WebSocket側のイベントで発生したオブジェクトが、TextFrameかつTextFrameがquit
       -      WebSocket側のイベントで発生したオブジェクトが、TextFrame
       -      roomMessagesStream側のイベントで発生したオブジェクトが、ChatRoom.Joinクラス
       -      roomMessagesStream側のイベントで発生したオブジェクトが、ChatRoom.Messageクラ
              ス
       -      roomMessagesStream側のイベントで発生したオブジェクトが、ChatRoom.Leaveクラス
       -      WebSocket側のイベントで発生したオブジェクトが、SocketClosed


  public	
  static	
  void	
  join(String	
  user)	
  {
  	
  	
  	
  	
  while(inbound.isOpen())	
  {
  	
  	
  	
  	
  	
  	
  	
  	
  Either<WebSocketEvent,ChatRoom.Event>	
  e	
  =	
   ここで停止
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  await(Promise.waitEither(
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  inbound.nextEvent(),	
  
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  roomMessagesStream.nextEvent()));
  	
  	
  	
  	
  	
  	
  	
  	
  ...各種処理...
  	
  	
  	
  	
  }
  }

            Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   27
ChatModelとChatRoomSocketでイベント判定

 ChatModelは、通常のモデルと違いイベントを管理
 ➡   内部にEventクラスというabstractなクラスを用意
 ➡   Eventクラスを継承した各種イベントクラス
  • Joinクラス
  • Leaveクラス
  • Messageクラス
 ChatRoomSocketでのイベント判定
 ➡   ChatRoom.Eventクラスをスーパークラスとするクラスを
     パターンマッチで分岐
     •   ClassOf(ChatRoom.Join.class).match(e._2)

     •   ClassOf(ChatRoom.Message.class).match(e._2)

     •   ClassOf(ChatRoom.Leave.class).match(e._2)

          Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   28
イベントはクラスで判別というのがセオリー?

イベントの全体像まとめ
➡   ChatRoomイベント
    •   ChatRoom.Joinクラス

    •   ChatRoom.Messageクラス

    •   ChatRoom.Leaveクラス
➡   WebSocketイベント
    •   WebSocketFrameクラス
        -   play.mvc.Http.WebSocketEvent.TextFrame
        -   play.mvc.Http.WebSocketEvent.BinaryFrame

    •   WebSocketCloseクラス
        -   play.mvc.Http.WebSocketEvent.SocketClosed




            Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   29
フロント側のイベントはどういう定義になっている?

フロント側のイベントスキーマ
➡   JavaScript側で定義し、socket.onmessageで取得できる
    dataプロパティの値によって振り分け

      イベント名:ユーザー名:メッセージテキスト


event.type
➡   message
➡   join
➡   leave
➡   quit



       Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   30
JavaScriptのWebSocket APIを把握しよう

 JavaScriptのWebSocketのAPI
 ➡   socket = WebSocket(url, protocols)
 ➡   socket.send()
 ➡   socket.close()

 イベントハンドラ
 ➡   socket.onmessage
 ➡   socket.onopen
 ➡   socket.onerror
 ➡   socket.onclose




        Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   31
SVGを使った応用例



 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   32
某Developers Day応募作品
 ➡   作成したアプリケーション




      Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   33
作品の解説

アプリケーションの設計
➡   ベースはサンプルチャットとほぼ同じ
➡   SVGをjQueryで操作
➡   ユーザー名はアクセスするたびに生成されるユニークID
➡   ロールオーバー時にWebSocketイベントを発生
➡   ロールオーバーした描画オブジェクトのIDをメッセージとして
    渡す
➡   アニメーションはCSS3のアニメーション




      Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   34
WebSocketController



  Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   35
Controllerとは少し違うWebSocketController

 WebSocketController
 ➡   通常のControllerのようにStaticメソッドによるアクションが
     定義可能
 ➡   ルーティングにより、URLの割当が可能
 ➡   InboundとOutboundという2種類のチャンネルが存在
 ➡   request、params、validation、sessionは存在
 ➡   disconnect()メソッドにより、通信の切断が可能
 ➡   awaitメソッドによる処理の待機
 ➡   ビューでは、@@{}タグによってwsプロトコルにすることが
     可能
     •   Routesでプロトコルの指定が必要




         Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   36
InboundとOutbound、2つのチャンネルがある

 InboundとOutbound
 ➡   Inbound
     •   WebSocketの受信

     •   WebSocketのソケットがオープンしている間、isOpen()がtrueを返す

     •   inbound.nextEvent()にて、発生したイベントを取得
 ➡   Outbound
     •   WebSocketの送信

     •   Outbound.send(String string)にて、WebSocketにて送信

     •   sendメソッドには、opcodeを指定した生データに近い送信も行える
         -   outbound.send(byte opcode, byte[] data);




             Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   37
WebSocketアプリケーションの基本モデル

 WebSocketアプリ:サーバーとクライアント

         DB                    PlayFramework                      WebSocketハンドシェイク



                           Inbound                                                                 Webサイト
                                                                             WebSocketソケット通信

           WebSocket
                                              Outbound
           Controller

アプリごと
にイベント           Model                WebSocket
デザインが           Event                  Event
必要
                  Join                TextFrame



              Message                SocketClose



                 Leave              BinaryFrame




        Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)        38
テキストフレーム以外も扱えるOutbound

 Outboundあれこれ
 ➡   テキストフレームは、通常下記みたいな利用をするが、
     •   outbound.send("quit:ok");
 ➡   JSONでも送信が可能。sendJsonを使用する
     •   outbound.sendJson(Object);
 ➡   生データ
     •   send(byte opcode, byte[] data)




          Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   39
これさえ覚えればOK、WebSocketのテキストフレーム

テキストフレームのやりとり
 ➡   受信
     •   play.mvc.Http.WebSocketEvent.TextFrameからMatcherを使って
         textDataを取り出す
 ➡   送信
     •   outbound.send(String string)を使って送信




          Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   40
F.javaの話



  Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   41
奇々怪々なコードが満載なF.java

 F.java
 ➡   WebSocket周りを見ていくと、必ずぶち当たる壁
 ➡   Javaで関数言語的な使い方をサポートするライブラリ
     •   Either
         -   E2、E3、E4、E5まである
         -   Haskel由来?

     •   ArchivedEventStream
         -   イベントのキューとして扱う仕組み

     •   Matcher
         -   パターンマッチ用

     •   Promise
         -   非同期処理

 ➡   F.javaを制するものはPlayを制す?!(まだ制してません


             Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   42
Haskel由来?なクラス

 Eitherについて
 ➡   どちらか1つの型が入っていた場合の返り値を受け取れる
 ➡   eという値で受け取った場合、e._1もしくはe._2が入る

 中身
 ➡   Either<WebSocketEvent, ChatRoom.Event> eのトレース
     •   E2(_1: Some(play.mvc.Http$WebSocketFrame@65202d8a), _2:
         None)

     •   E2(_1: None, _2: Some(models.ChatRoom$Message@238df2e4))
 ➡   E2(_1: 値, _2: 値)という出力が出るtoString()が実装
     •   これで学習用の暫定確認が行える




          Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   43
WebSocketで見られる不思議なfor構文の正体

 パターンマッチFor文の使い方
   for(String	
  a:	
  TextFrame.match(e._1))	
  {
   	
  	
  	
  	
  ...処理...
   }

   for(String	
  a:	
  TextFrame.and(Equals("quit"))
   .match(e._1))	
  {
   	
  	
  	
  	
  ...処理...
   }

   for(Message	
  a:	
  ClassOf(Message.class).match(e._2))	
  {
   	
  	
  	
  	
  ...処理...
   }


  明示的なキャストが存在しないので、すべてがタイプセーフであり、コンパイラ
  によって型チェックが行われます。

        Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   44
Matcherは最重要

 F.javaのMatcher周りについて
 ➡   String
     •   文字列でマッチ
 ➡   ClassOf
     •   クラスでマッチ
 ➡   StartsWith
     •   プレフィックスでマッチ
 ➡   Re
     •   正規表現パターンでマッチ
 ➡   Equals
     •   等価な文字列でマッチ



          Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   45
WebSocketEventは下記のMatcherを覚える

 WebSocketEvent周りのMatcher
 ➡   SocketClosed.match()
     •   WebSocketCloseかどうかでマッチ
 ➡   TextFrame.match()
     •   WebSocketフレームがバイナリでなく、なおかつtextDataでマッチ
 ➡   BinaryFrame.match()
     •   WebSocketフレームがバイナリで、binaryDataでマッチ




         Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   46
WebSocketアプリケーション
の設計・構築手順



 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   47
こうやればスムーズ?WebSocketアプリケーションの設計

WebSocketアプリケーションの設計・構築手順
 ➡   ベースとなるバックエンドのPlayアプリケーションの作成
 ➡   フロント側のイベントスキーム(JavaScriptのイベント)を決定
 ➡   フロント側で受け取るフォーマット形式の作成
 ➡   バックエンド側のイベントスキーム(モデル)を決める
 ➡   WebSocketコントローラーをつくり、バックエンド側の
     イベントスキームをマッチするように組み込む
 ➡   WebSocketのルーティングの設定
 ➡   イベントマッチ時のメッセージの出力実装
 ➡   フロント側でのAjaxの実装
 ➡   テスト送信をし、テスト

     クラスベースハンドラのイベント駆動プログラム関数型風味

       Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   48
通信の確認

WebSocket通信時の確認にはChromeが便利




    Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   49
まとめ



 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   50
まとめ




    Play!のWebSocketControllerは
    チャットを作るための機能ではない



WebSocketアプリケーション
設計のコツ(エコー系)
➡   テキストフレームのスキーマ設計
➡   イベント設計
➡   インバウンド後の処理設計
➡   アウトバウンドのタイミング設計


      Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   51
このへん、議論したいです

WebSocketはどういうケースで役に立つのか?
➡   ログインをした移行は、ユーザーによる細かな操作が続く場合
➡   Ajaxで行ってきた部分の上位バージョンとして使われる?
➡   ブラウザで閲覧しているどこかのタイミングでリアルタイム
    通信が必要なとき
    .....




                             勉強会で議論していきましょう




     Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   52
今後勉強会でテーマにしていきたいこと

今後のお題
➡   WebSocketの利用範囲を考える考察
➡   既存の通信手段とのパフォーマンス比較
➡   outbound.sendJson()の実験
➡   MessagePackの利用
➡   認証がからむケースのセキュアなWebSocket
➡   WebSocketのデータを保存していくベストプラクティス
➡   イベントの登録管理をもっとスマートに
➡   PlayにおけるWebSocketアプリケーションの負荷の調査
➡   サーバー構成の研究
➡   ネームスペースの実装?(Socket.IO)


      Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   53
ありがとうございました


PlayFramework + WebSocket 勉強会
今後も定期的に勉強会をします。#playbay にて情報を発信していきます。
ハンズオン中心でやろうかと。
わからないことを聞きやすいけど、ハックもがんがんする的な感じを目指してます。




   Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/)   54

PlayFramework1.2.4におけるWebSocket

  • 1.
    エンジニアカフェ PlayFramework + WebSocket勉強会 PlayFramework1.2.4におけるWebSocket 原 一浩 @kara_d
  • 2.
    原 一浩 @kara_d $  play  netbeansify
  • 3.
    何してる人? Greative is Great Creative トレンド 、統計、デザイン、システム デザイントレンドリサーチをベースに、デザインされたアプリ PlayFramework、R言語、CakePHP、Flexなど受託制作
  • 4.
    4/4のJavaOneに参戦!! JavaOneでスピーカーします ➡ PlayFramework + WebSocketでつくる リアルタイムWebアプリケーション Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 4
  • 5.
    アジェンダ Copyright(C) 2012Greative - Sustainable Automation, for Creative -(http://greative.jp/) 5
  • 6.
    本日のゴールと目次 本日のゴール ➡ なんとなくWebSocketアプリケーションが作れるくらい ➡ PlayFrameworkにおけるWebSocketControllerの全体理解 目次 ➡ フロントエンドから見た、リアルタイム通信サーバのこれまで ➡ WebSocketについて ➡ WebSocket未対応ブラウザへの対応 ➡ PlayFrameworkとWebSocket ➡ Sample chatでみる、WebSocketアプリの構成 ➡ WebSocketController ➡ F.javaの話 ➡ WebSocketアプリケーションの設計・構築手順 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 6
  • 7.
    フロントエンドから見た、 リアルタイム通信サーバの これまで Copyright(C) 2012Greative - Sustainable Automation, for Creative -(http://greative.jp/) 7
  • 8.
    昔々のリアルタイム通信コンテンツの定番 Flashでのリアルタイム通信 ➡ Adobe Flash Communication Server ➡ Adobe Flash Media Server Real Time Messaging Protocol(RTMP) ➡ Red5 • http://www.red5.org/ • Javaで作られている • オープンソース Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 8
  • 9.
    WebSocketについて Copyright(C) 2012Greative - Sustainable Automation, for Creative -(http://greative.jp/) 9
  • 10.
    WebSocketの仕様について WebSocketは、W3CとIETFが策定している ➡ 元々はHTML5の仕様の一部 ➡ 双方向の通信技術 ➡ AjaxやCommetよりもプッシュ技術の仕組みとして自然 ➡ 一度コネクションを行った後は専用の通信プロトコルで通信 • ハンドシェイク • ws:もしくはwss: ➡ 最終仕様は、RFC6455。対応は下記(※は未調査) • IE 10 Platform Preview 5※ • Firefox 11 • Google Chrome 16 • Safari 最新※ Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 10
  • 11.
    WebSocket未対応ブラウザへ Copyright(C) 2012Greative - Sustainable Automation, for Creative -(http://greative.jp/) 11
  • 12.
    クロスブラウザ対応の力強い味方 web-socket-js ➡ https://github.com/gimite/web-socket-js • Google Chrome 4 or later, Firefox 6 or later (uses native WebSocket or MozWebSocket implementation) • Firefox 3 to 5, Internet Explorer 8, 9 + Flash Player 10 or later Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 12
  • 13.
    web-socket-jsのインストール方法(1) PlayFrameworkでの利用 ➡ ダウンロードしたら、 • public/javascripts/ に「swfobject.js」「web_socket.js」を配置 • public/swfs/ に「WebSocketMain.swf」を配置 ➡ main.html(レイアウト)に記述 <script  src="@{'/public/javascripts/swfobject.js'}"   type="text/javascript"  charset="$ {_response_encoding}"></script> <script  src="@{'/public/javascripts/web_socket.js'}"   type="text/javascript"  charset="$ {_response_encoding}"></script> Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 13
  • 14.
    web-socket-jsのインストール方法(2) JavaScriptでのWebSocketの接続方法 WEB_SOCKET_SWF_LOCATION  =          "@{'/public/swfs/WebSocketMain.swf'}"; WEB_SOCKET_DEBUG  =  false; var  socket; socket  =  new  WebSocket(        '@@{WebSocket.ChatRoomSocket.join(user)}') ➡ Flashを介している点がデフォルトの方法と異なる ➡ こうすることでFirefoxなど未対応ブラウザでも動作可能 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 14
  • 15.
    PlayFrameworkと WebSocket Copyright(C)2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 15
  • 16.
    PlayFrameworkとWebSocket戦いの歴史 Play1.2.3 Play勉強会 ➡ hybi-07 ➡ version 7(だったような...) ➡ Chrome 14までサポート Play1.2.4 ➡ hybi-10 ➡ バージョン8 ➡ Chrome 16までサポート パッチ(Lighthouse 1240 patch) Play合宿 Play, WebSocket and CakePHP mini hack-a-thon 雪山合宿 ➡ RFC6455 ➡ 最新版に対応 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 16
  • 17.
    パッチを当てれば動く!! パッチ(Lighthouse 1240 patch)の当て方 ➡ https://github.com/playframework/play/pull/438 を参照 ➡ framework/src/play/server/PlayHandler.java を修正 ➡ 修正後、antタスクを実行 $  ~/play-­‐1.2.4/framework $  ant   Buildfile:  /Users/harakazuhiro/play-­‐1.2.4/framework/ build.xml ..... BUILD  SUCCESSFUL Total  time:  22  seconds Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 17
  • 18.
    パッチがあたると、こうなる パッチが当たったリビルド版 ➡ バージョン表記が「play! 1.2.x-localbuild」に $  play  run                                                                                             ~                _                        _   ~    _  __  |  |  __  _  _    _|  | ~  |  '_  |  |/  _'  |  ||  |_| ~  |    __/|_|____|__  (_) ~  |_|                        |__/       ~ ~  play!  1.2.x-­‐localbuild,  http://www.playframework.org Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 18
  • 19.
    パッチについてのご意見 パッチ(Lighthouse 1240 patch)に関する情報 @gtk2k さんより @kara_d versionにはクライアントのSec-WebSocketフィールドの値 は、  versionにはクライアントのSec-WebSocket-Versionフィールドの値 の間違い です。 res.setHeader("Sec-WebSocket-Version", "13"); ➡ が正解? Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 19
  • 20.
    PlayFramework以外の状況 Play以外は追いかけれていませんが、ご参考まで • Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 20
  • 21.
    MVCとポートの話 WebSocketをMVCフレームワークに載せる意味 ➡ Webアプリケーションとのシームレスな統合 ➡ MVCフレームワークでWebSocketもコントローラーとして ラッピングすることで、設計が容易に ➡ 認証系、データ管理系の実装が楽になる • 負荷を考えると結局は別サーバー? WebSocketアプリケーションとポート ➡ 基本的には、Playが動いているポートになる ➡ Apacheをフロントに置いて、mod_proxyを経由させている 場合は注意 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 21
  • 22.
    接続先は自動で生成可能 PlayFrameworkとJavaScriptとの連携 ➡ WebSocketのポート、アドレスの出力 socket  =          new  WebSocket(                '@@{WebSocket.ChatRoomSocket.join(user)}'        ) とすると、 socket  =          new  WebSocket(                'ws://localhost:9000/websocket/room/socket? user=hara'        ) と出力される。 • @@{}で絶対URLになる Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 22
  • 23.
    Sample chatでみる、 WebSocketアプリの構成 Copyright(C)2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 23
  • 24.
    付属のサンプル話 PlayFrameworkでのWebSocketのサンプル ➡ samples-and-tests/chat • Reflesh形式 • Commet形式 • WebSocket形式 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 24
  • 25.
    WebSocketアプリケーションの基本モデル WebSocketアプリ:サーバーとクライアント DB PlayFramework WebSocketハンドシェイク Webサイト Inbound WebSocketソケット通信 WebSocket Outbound Controller アプリごと にイベント Model WebSocket デザインが Event Event 必要 Join TextFrame Message SocketClose Leave BinaryFrame Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 25
  • 26.
    サンプルのチャットアプリケーションを読み解く(1) 全体構成 ➡ クライアント • コマンドを「:」でつないだシンプルなメッセージ方式 • コマンド : ユーザー : メッセージ • message : name : text ➡ サーバー • WebSocketコントローラー内にChatRoomSocketという WebSocketControllerクラスを継承したクラスがある • ChatRoomSocketには、joinメソッドのみがある • ChatRoomモデル内でチャットのイベントを設定 • roomMessagesStreamイベントストリームを作成 • inbound.isOpen()の限りループ - ただし、Either<WebSocketEvent,ChatRoom.Event> eに値が入らない限りは停止 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 26
  • 27.
    サンプルのチャットアプリケーションを読み解く(2) • 各種イベントにマッチするものがあれば、各処理を実行 - WebSocket側のイベントで発生したオブジェクトが、TextFrameかつTextFrameがquit - WebSocket側のイベントで発生したオブジェクトが、TextFrame - roomMessagesStream側のイベントで発生したオブジェクトが、ChatRoom.Joinクラス - roomMessagesStream側のイベントで発生したオブジェクトが、ChatRoom.Messageクラ ス - roomMessagesStream側のイベントで発生したオブジェクトが、ChatRoom.Leaveクラス - WebSocket側のイベントで発生したオブジェクトが、SocketClosed public  static  void  join(String  user)  {        while(inbound.isOpen())  {                Either<WebSocketEvent,ChatRoom.Event>  e  =   ここで停止                        await(Promise.waitEither(                                inbound.nextEvent(),                                  roomMessagesStream.nextEvent()));                ...各種処理...        } } Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 27
  • 28.
    ChatModelとChatRoomSocketでイベント判定 ChatModelは、通常のモデルと違いイベントを管理 ➡ 内部にEventクラスというabstractなクラスを用意 ➡ Eventクラスを継承した各種イベントクラス • Joinクラス • Leaveクラス • Messageクラス ChatRoomSocketでのイベント判定 ➡ ChatRoom.Eventクラスをスーパークラスとするクラスを パターンマッチで分岐 • ClassOf(ChatRoom.Join.class).match(e._2) • ClassOf(ChatRoom.Message.class).match(e._2) • ClassOf(ChatRoom.Leave.class).match(e._2) Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 28
  • 29.
    イベントはクラスで判別というのがセオリー? イベントの全体像まとめ ➡ ChatRoomイベント • ChatRoom.Joinクラス • ChatRoom.Messageクラス • ChatRoom.Leaveクラス ➡ WebSocketイベント • WebSocketFrameクラス - play.mvc.Http.WebSocketEvent.TextFrame - play.mvc.Http.WebSocketEvent.BinaryFrame • WebSocketCloseクラス - play.mvc.Http.WebSocketEvent.SocketClosed Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 29
  • 30.
    フロント側のイベントはどういう定義になっている? フロント側のイベントスキーマ ➡ JavaScript側で定義し、socket.onmessageで取得できる dataプロパティの値によって振り分け イベント名:ユーザー名:メッセージテキスト event.type ➡ message ➡ join ➡ leave ➡ quit Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 30
  • 31.
    JavaScriptのWebSocket APIを把握しよう JavaScriptのWebSocketのAPI ➡ socket = WebSocket(url, protocols) ➡ socket.send() ➡ socket.close() イベントハンドラ ➡ socket.onmessage ➡ socket.onopen ➡ socket.onerror ➡ socket.onclose Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 31
  • 32.
    SVGを使った応用例 Copyright(C) 2012Greative - Sustainable Automation, for Creative -(http://greative.jp/) 32
  • 33.
    某Developers Day応募作品 ➡ 作成したアプリケーション Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 33
  • 34.
    作品の解説 アプリケーションの設計 ➡ ベースはサンプルチャットとほぼ同じ ➡ SVGをjQueryで操作 ➡ ユーザー名はアクセスするたびに生成されるユニークID ➡ ロールオーバー時にWebSocketイベントを発生 ➡ ロールオーバーした描画オブジェクトのIDをメッセージとして 渡す ➡ アニメーションはCSS3のアニメーション Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 34
  • 35.
    WebSocketController Copyright(C)2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 35
  • 36.
    Controllerとは少し違うWebSocketController WebSocketController ➡ 通常のControllerのようにStaticメソッドによるアクションが 定義可能 ➡ ルーティングにより、URLの割当が可能 ➡ InboundとOutboundという2種類のチャンネルが存在 ➡ request、params、validation、sessionは存在 ➡ disconnect()メソッドにより、通信の切断が可能 ➡ awaitメソッドによる処理の待機 ➡ ビューでは、@@{}タグによってwsプロトコルにすることが 可能 • Routesでプロトコルの指定が必要 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 36
  • 37.
    InboundとOutbound、2つのチャンネルがある InboundとOutbound ➡ Inbound • WebSocketの受信 • WebSocketのソケットがオープンしている間、isOpen()がtrueを返す • inbound.nextEvent()にて、発生したイベントを取得 ➡ Outbound • WebSocketの送信 • Outbound.send(String string)にて、WebSocketにて送信 • sendメソッドには、opcodeを指定した生データに近い送信も行える - outbound.send(byte opcode, byte[] data); Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 37
  • 38.
    WebSocketアプリケーションの基本モデル WebSocketアプリ:サーバーとクライアント DB PlayFramework WebSocketハンドシェイク Inbound Webサイト WebSocketソケット通信 WebSocket Outbound Controller アプリごと にイベント Model WebSocket デザインが Event Event 必要 Join TextFrame Message SocketClose Leave BinaryFrame Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 38
  • 39.
    テキストフレーム以外も扱えるOutbound Outboundあれこれ ➡ テキストフレームは、通常下記みたいな利用をするが、 • outbound.send("quit:ok"); ➡ JSONでも送信が可能。sendJsonを使用する • outbound.sendJson(Object); ➡ 生データ • send(byte opcode, byte[] data) Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 39
  • 40.
    これさえ覚えればOK、WebSocketのテキストフレーム テキストフレームのやりとり ➡ 受信 • play.mvc.Http.WebSocketEvent.TextFrameからMatcherを使って textDataを取り出す ➡ 送信 • outbound.send(String string)を使って送信 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 40
  • 41.
    F.javaの話 Copyright(C)2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 41
  • 42.
    奇々怪々なコードが満載なF.java F.java ➡ WebSocket周りを見ていくと、必ずぶち当たる壁 ➡ Javaで関数言語的な使い方をサポートするライブラリ • Either - E2、E3、E4、E5まである - Haskel由来? • ArchivedEventStream - イベントのキューとして扱う仕組み • Matcher - パターンマッチ用 • Promise - 非同期処理 ➡ F.javaを制するものはPlayを制す?!(まだ制してません Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 42
  • 43.
    Haskel由来?なクラス Eitherについて ➡ どちらか1つの型が入っていた場合の返り値を受け取れる ➡ eという値で受け取った場合、e._1もしくはe._2が入る 中身 ➡ Either<WebSocketEvent, ChatRoom.Event> eのトレース • E2(_1: Some(play.mvc.Http$WebSocketFrame@65202d8a), _2: None) • E2(_1: None, _2: Some(models.ChatRoom$Message@238df2e4)) ➡ E2(_1: 値, _2: 値)という出力が出るtoString()が実装 • これで学習用の暫定確認が行える Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 43
  • 44.
    WebSocketで見られる不思議なfor構文の正体 パターンマッチFor文の使い方 for(String  a:  TextFrame.match(e._1))  {        ...処理... } for(String  a:  TextFrame.and(Equals("quit")) .match(e._1))  {        ...処理... } for(Message  a:  ClassOf(Message.class).match(e._2))  {        ...処理... } 明示的なキャストが存在しないので、すべてがタイプセーフであり、コンパイラ によって型チェックが行われます。 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 44
  • 45.
    Matcherは最重要 F.javaのMatcher周りについて ➡ String • 文字列でマッチ ➡ ClassOf • クラスでマッチ ➡ StartsWith • プレフィックスでマッチ ➡ Re • 正規表現パターンでマッチ ➡ Equals • 等価な文字列でマッチ Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 45
  • 46.
    WebSocketEventは下記のMatcherを覚える WebSocketEvent周りのMatcher ➡ SocketClosed.match() • WebSocketCloseかどうかでマッチ ➡ TextFrame.match() • WebSocketフレームがバイナリでなく、なおかつtextDataでマッチ ➡ BinaryFrame.match() • WebSocketフレームがバイナリで、binaryDataでマッチ Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 46
  • 47.
    WebSocketアプリケーション の設計・構築手順 Copyright(C) 2012Greative - Sustainable Automation, for Creative -(http://greative.jp/) 47
  • 48.
    こうやればスムーズ?WebSocketアプリケーションの設計 WebSocketアプリケーションの設計・構築手順 ➡ ベースとなるバックエンドのPlayアプリケーションの作成 ➡ フロント側のイベントスキーム(JavaScriptのイベント)を決定 ➡ フロント側で受け取るフォーマット形式の作成 ➡ バックエンド側のイベントスキーム(モデル)を決める ➡ WebSocketコントローラーをつくり、バックエンド側の イベントスキームをマッチするように組み込む ➡ WebSocketのルーティングの設定 ➡ イベントマッチ時のメッセージの出力実装 ➡ フロント側でのAjaxの実装 ➡ テスト送信をし、テスト クラスベースハンドラのイベント駆動プログラム関数型風味 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 48
  • 49.
    通信の確認 WebSocket通信時の確認にはChromeが便利 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 49
  • 50.
    まとめ Copyright(C) 2012Greative - Sustainable Automation, for Creative -(http://greative.jp/) 50
  • 51.
    まとめ Play!のWebSocketControllerは チャットを作るための機能ではない WebSocketアプリケーション 設計のコツ(エコー系) ➡ テキストフレームのスキーマ設計 ➡ イベント設計 ➡ インバウンド後の処理設計 ➡ アウトバウンドのタイミング設計 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 51
  • 52.
    このへん、議論したいです WebSocketはどういうケースで役に立つのか? ➡ ログインをした移行は、ユーザーによる細かな操作が続く場合 ➡ Ajaxで行ってきた部分の上位バージョンとして使われる? ➡ ブラウザで閲覧しているどこかのタイミングでリアルタイム 通信が必要なとき ..... 勉強会で議論していきましょう Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 52
  • 53.
    今後勉強会でテーマにしていきたいこと 今後のお題 ➡ WebSocketの利用範囲を考える考察 ➡ 既存の通信手段とのパフォーマンス比較 ➡ outbound.sendJson()の実験 ➡ MessagePackの利用 ➡ 認証がからむケースのセキュアなWebSocket ➡ WebSocketのデータを保存していくベストプラクティス ➡ イベントの登録管理をもっとスマートに ➡ PlayにおけるWebSocketアプリケーションの負荷の調査 ➡ サーバー構成の研究 ➡ ネームスペースの実装?(Socket.IO) Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 53
  • 54.
    ありがとうございました PlayFramework + WebSocket勉強会 今後も定期的に勉強会をします。#playbay にて情報を発信していきます。 ハンズオン中心でやろうかと。 わからないことを聞きやすいけど、ハックもがんがんする的な感じを目指してます。 Copyright(C) 2012 Greative - Sustainable Automation, for Creative -(http://greative.jp/) 54