Websocketに最近はまっています。
前回プッシュ配信をRedisのPub/Subを用いて実現した訳ですが、データ量が増えてくるとどうしても配信するデータを圧縮したくなります。
特にAWSなんかだと、従量課金となっていますので、データ転送量は少なければ少ないほど○
Websocket自体はまだデータの圧縮はサポートされていないとのことですので、自前でデータの圧縮伸長を実装してみました。
また、それだけではつまらないので、Reidsにチャンネルを作成し、ReidsのチャンネルごとにPub/Subする仕組みを入れています
図解するとちょっとわかりにくいですがこんな感じ
user1 userActor redisActor sub Redis |code1,code2| --- | user1 | --- | code1 | --- | | | | | user2 | | --- | code2 | --- | |code2,code3| --- | user2 | --- | code3 | --- |
User別に作成したActorと、ユーザが選択したチャンネル(code1,code2,code3)別に作成したActorを別々に作成し、チャンネルがReidsから更新された際には、購読しているUserのActorへ更新をかけるという感じです
こうすることにより、チャンネル数がユーザ数より圧倒的に少ない場合には、Reidsに対するコネクションも削減でき、効率が良くなります。
- route
GET /board controllers.Application.board(code:Option[String]) # 画面作成用 GET /board/data controllers.Application.data(code) #WSでデータ取得用 GET /asset/javascripts/board.js #Javascript controllers.Application.boardJs(code:String)
- Compress.scala
データ圧縮用に作成します。String型をZIP圧縮し、BASE64でエンコードします
package models import java.util.zip._ import java.io._ import org.apache.commons.codec.binary._ object Compress { def encode(str:String):String={ val out = new ByteArrayOutputStream() val defl = new DeflaterOutputStream(out, new Deflater(Deflater.BEST_COMPRESSION, true)) defl.write(str.getBytes()) defl.close() val compressed = Base64.encodeBase64(out.toByteArray()) new String(compressed) } }
- UserActor.scala
... def notifyAll(code:String,data:String){ // 実際にWebSocketでブラウザに送るデータ val msg=JsObject( Seq( "code"->JsString(code), "data"->JsString(Compress.encode(data)) // JSONの一部データ部分のみ圧縮 ..
- Javascriptで解凍
JavaScriptでZIP解凍、Base64でコードするために↓からinfrate.js,base64.js,utf.jsなどをダウンロードしておきます。
http://www.onicos.com/staff/iz/amuse/javascript/expert/
@(code:String)(implicit r: RequestHeader) $(function() { var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket var socket = new WS("@routes.Application.data(code).webSocketURL()") var receiveEvent = function(event) { var data = JSON.parse(event.data) var bs=base64decode(data.data) // Base64デコード var dec=zip_inflate(bs) // ZIP解凍 var p=JSON.parse(dec) $("#data").text(p.data); } socket.onmessage = receiveEvent })
サンプルの一式は以下においておきました