dieselでかんたんWebSocketプログラミングWebSocket programming with diesel

今回は,dieselというフレームワークを紹介し,dieselとWebSocketを利用した簡単なアプリケーションの実例を示したいと思います.

dieselとは

dieselは,非同期処理にフォーカスしたアプリケーションフレームワークです.Pythonで書かれており,現在公開されているdiesel 3.0はBumpの裏側で動いているコードをベースとしているようです.オフィシャルサイト上では,次のような特徴を掲げています.

  • 高いスケーラビリティ
    • 10000コネクション以上にも対応可能,かつ省メモリ
  • コードが書きやすい
    • コールバックを使うことなく,通常の非同期でないコードを書く感覚で非同期処理を実現できる
  • 各種プロトコルやフレームワーク等を標準で利用可能
    • FlaskやWebSocketをはじめとして,Redis, ZeroMQ, MongoDB, Riakが,プラグインの追加等を行うことなく標準で利用可能

個人的には,次のような点が便利だと感じています.

  • 関数にデコレータを付けるだけで,WebSocketのサーバサイドアプリケーションが簡単に実現できる
  • いわゆる”Fanout”なキューが標準で利用できる

公式サイトのトップページにあるWebSocketを使ったチャットとそのコードを見ていただければ,dieselの簡単さが分かるかと思います. もちろん,より低レイヤなTCP/UDPレベルでの非同期通信も利用可能です.利用方法については,オフィシャルサイトのチュートリアルにいくつか典型的 なものが掲載されています.

dieselの利用例: Android端末の現在位置をリアルタイムでブラウザに表示

ここでは,dieselのWebSocketを利用したアプリケーションの例として,Android端末の現在位置をリアルタイムでブラウザ上に表示するアプリケーションを紹介したいと思います.

今回紹介するアプリケーションは,1月の学会発表先日のオープンキャンパス等 での研究デモにおいて実際に使用しているものです.研究デモで紹介しているAndroidアプリは各端末の位置関係が動作の上で重要なポイントとなってお り,このことをデモブースの来訪者に対して分かりやすく示す必要があります.そこで,複数のAndroid端末の現在位置(緯度,経度)を地図上にマッピ ングして情報表示用のモニタ(ラップトップPCやタブレット端末)に表示することで,各端末の位置関係を一目で把握できるようにしています.

まずは動かしてみる

最初に,dieselをpipで導入します.

次に,今回紹介しているアプリケーションのソースコードをダウンロードします.下記リンクからダウンロードして展開するか,gitリポジトリからcloneしてください.

diesel-realtime-locator.tar.gz

ソースコードが入手できたら,サーバ側のプログラムを実行します.

ブラウザから http://localhost:8080/map にアクセスすると,Google Mapsが埋め込まれたページが表示されるはずです.このページは開いたままにしておき,この状態でPCからテストしてみます.

適当なHTTPクライアントで位置情報更新用のURLにアクセスします.ここではcurlを使っています.

先ほど開いたままにしておいたGoogle Maps上で,2つのピンが平行して移動していれば成功です.

map_pin_moving

最後にAndroid端末で試してみます.クライアント側のプログラムclient-sl4a.pyをAndroid端末上で実行します.なお,このプログラムの実行にはSL4AのインストールとPythonインタプリタの追加が必要です.インストール方法の詳細は,下記ページを参照してください.

client/client-sl4a.pyを実行すると,サーバ側のホスト名とポート番号,端末名(“Node name”)の入力を求められます.端末名については,地図へのマッピングの際に端末の識別に使っているものですので,任意の文字列で構いません.

sl4a_name sl4a_server

先ほど開いたままにしておいたGoogle Maps上に端末の現在位置を示すピンが表示されれば成功です.

map_pin_sl4a

各部解説

サーバ側アプリケーション (server/server.py)

サーバ側のコードは,dieselの公式サイトトップページにあるチャットのサンプルとほぼ同一です.異なるのは,Android端末からサーバへの位置情報送信をWebSocketではなくGETメソッドにしているぐらいです.

サーバ側のコードは大まかに2パートに分けられます.ひとつはAndroid端末からの位置情報受信を行う update_handler(), もうひとつはブラウザへの位置情報送信をWebSocket経由で行う ws_handler() です.

update_handler()は,GETメソッドで送られてきた端末の情報を(ブラウザがJSONとして扱えるよう)dictに詰め,Fanoutなキューにpublishします.

ws_handler()は,HTTPヘッダ等を含むrequest,WebSocketクライアント(ブラウザ)から送信された情報が挿入されるキューであるinq,WebSocketクライアントに送信する情報を挿入するキューであるoutqの3つが引数として与えられます. 関数内では,まずFanoutなキューであるroutergroupとしてsubscribeし,first()groupinqにデータが到着するのを(非同期に)待ちます. first()はイベントを表現するev (この場合はデータが到着したキューのインスタンス) と到着した値 val からなるtupleを返すので,ev の値によって処理を振り分けます.今回のアプリケーションではAndroid端末から送信された情報をブラウザに送信したいので,evgroup,つまり update_handler() においてpublishされたものであれば,valoutqput() しています.

チャットのサンプルのようにクライアント – サーバ間の両方向ともWebSocketにしてもよかったのですが,そうするとAndroid端末上でのクライアント実装のコストが若干上がってしまうため,今回はGETメソッドを用いました.ここは,今後の改良点です.

Google Mapsほか (server/templates/map.html)

ブラウザで http://SERVER/map にアクセスすると,map.htmlの中身が出力されるようになっています.map.html内では,WebSocket経由で各端末の位置情報を受け取り,地図上のピンの位置を更新しています.

位置が変化した(=移動した)端末に対応するピンにバルーンが表示されたり,ピンが色分けできたりすると,より分かりやすくなりそうです.

Android向けクライアント (client-sl4a.py)

クライアント側のコードは,GPSから得られた緯度・経度をサーバに送信する処理を行います.ネイティブアプリとして実装するほどでもないので,Pythonで実装しています.

通常,SL4Aで位置情報を取得する場合,startLocating() / readLocation() / stopLocating()を使うのですが,ここではgetLastKnownLocation()を使っています.これは,実際のデモにおいては疑似ロケーションを 提供するアプリをバックグラウンドで使用していることに関係しています.デモ環境は通常屋内である関係上,GPSが受信できません.そのた め,Android端末の物理的な移動を疑似ロケーションで再現しています.これにより,常に最新の(擬似的な)位置が確実に取得できるため,getLastKnownLocation()を使用しています.通常は,冒頭の3メソッドを使用してください.

おわりに

dieselを使うと,WebSocketを使ったリアルタイムなアプリケーションが簡単に実現できることがお分かりいただけたかと思いま す.diesel自体は,まだまだドキュメントが未整備であるなど使い辛い点もあるのですが,WebSocket等が簡単に扱えるのは大きな魅力です.
また,冒頭の研究デモのような場合,可視化を行うことにより,多くの人に興味を持ってもらえるだけでなく,説明もやりやすくなります.ぜひ,試してみてください.

By Yohei KANEMARU