2015年9月4日金曜日

Play2のbroadcastとunicast


Play2の公式サイトのWebSocketの解説を読んでいるとConcurrent.broadcastConcurrent.unicastが出てきますよね。あまり詳しく書かれてなくて、良くわかんないので、ちょっとだけ自分でイジってみました。

その前にまずは、IterateeEnumeratorを簡単に復讐しておきます。Iterateeとは、何かしらの入力が与えられたときに行う処理を保持しているものでした。例えば、入力を足し上げていく処理は、次のように書けます。

val iteratee = Iteratee.fold( 0 ) { (sum, x) => sum + x }

そして、Enumeratorは、Iterateeに入力を与えるものでした。例えば、1から10までの数字をIterateeに入力するには、次のようにします。

val enum = Enumerator.enumerate( 1 to 10 )
val result: Future[Int] = enum.run( iteratee )

結果は、Futureとして取得されます。

result.onComplete( println ) // 55が出力される

以上がIterateeEnumratorです。

では、broadcastunicastは、IterateeEnumratorとどう関係しているのでしょうか?broadcastunicastがやりたいことってのは、基本的には同じです。すなわち、Channelを介してEnumeratorに入力を与えることです。つまり、ChannelEnumerateeに入力を与え、Enumerateeは、Iterateeに入力を与えるという関係になっているんです。

では、実際にbroadcastを使ってみましょう。次の例では、1から4までの数字をChannelを介してEnumeratorに与えている処理を書いてみました。

10 
11 
12 
13 
14 
15 
16 
// Enumeratorとchannelを生成
val (enum, channel) = Concurrent.broadcast[Int]

// Iteratorを実行
val result = enum.run( Iteratee.fold[Int, Int](0) { (sum, x) => sum + x })

// Enumeratorに入力を与える
channel.push( Input.El(1) )
channel.push( Input.El(2) )
channel.push( Input.El(3) )
channel.push( Input.El(4) )
// 入力終わり
channel.end

// 10が出力される
println( Await.result( result, 5.second ) )

上記のコードを見てもらえばわかるように、broadcastを使えば、channel.pushEnumeratorに後から入力を追加することができるんです。これが、broadcastunicastに共通している特徴だと思います。

次に、unicastで同じことしてみましょう。

10 
11 
12 
13 
14 
15 
16 
17 
// Enumeratorとchannelを生成
val enum = Concurrent.unicast[Int] { channel => 

// Enumeratorに入力を与える
channel.push( Input.El(1) )
channel.push( Input.El(2) )
channel.push( Input.El(3) )
channel.push( Input.El(4) )
channel.end

}

// Iteratorを実行
val result = enum.run( Iteratee.fold[Int, Int](0) { (sum, x) => sum + x })

// 10が出力される
println( Await.result( result, 5.second ) )

broadcastとの違いは、unicastは、Enumeratorを返すけど、Channelは返さない点です。Channelを、みんなが使えないようにしているんですね。使えるスコープを限定してChannelを共有できないようにしています。これが違いです。おそらく名前の違いも、このことが由来なんではないでしょうか?

以上、broadcastunicastの違いでした。

0 件のコメント:

コメントを投稿