Skip to content

Instantly share code, notes, and snippets.

@niratama
Last active December 24, 2015 11:19
Show Gist options
  • Save niratama/6790092 to your computer and use it in GitHub Desktop.
Save niratama/6790092 to your computer and use it in GitHub Desktop.

MojoliciousでリアルタイムWeb

こばやし けんいち

自己紹介 ねこ

  • こばやし けんいち @Niratama
  • Perlでサーバーサイドプログラム書いてます
  • が、最近はサーバのお守りまでやってる気が

いや本当は

  • Raspberry Pi使ったネタがやりたかったんだけど
  • 時間足りなかったので手軽なところで……

Mojoliciousについて

  • "Real-time web framework"
  • 基本的にはMojoliciousだけインストールすれば使える
  • サーバーサイドだけでなくクライアント側を書くのにも便利
  • コマンドラインやワンライナーのための仕組みが用意されている
  • 日刊Mojolicious」にハマるとちょっと困る

リアルタイムWebしてみる

Mojo::IOLoopを使ったノンブロッキング処理

  • 内蔵のWebサーバを使っていればMojo::IOLoopを使ったノンブロッキング処理が使える
  • timerなんかも使える←使い道は?

デモ:atndlist.pl

  • TwitterのIDからプロフィール情報とATNDのイベント参加履歴を取得して表示するデモ

atndlist.pl(1)

  Mojo::IOLoop->delay(
    # 並列処理の準備
    sub {
      my $delay = shift;

      # APIのURL設定(中略)
      # それぞれのAPIを呼び出す→レスポンスはdelayで受け取る
      $self->ua->get($twitter_api => $delay->begin);
      $self->ua->get($atnd_api => $delay->begin);
    },

atndlist.pl(2)

    # delayの値が揃った時に実行される処理
    sub {
      my ($delay, $twitter, $atnd) = @_;
      $self->render(
        template => 'search',
        twitter_id => $id,
        user => $twitter->res->json,
        events => $atnd->res->json->{events}
      );
    }
  );

WebSocket

  • RFC 6455
  • WebアプリケーションとWebサーバとの双方向通信

デモ:reverse.pl

  • クリックした升目をWebSocketで通信して、複数のブラウザでリアルタイム共有するデモ

reverse.pl(1)

  # コネクションのタイムアウトを長めにする
  Mojo::IOLoop->stream($self->tx->connection)->timeout(300);
  # MyEventのreceiveイベント受信時の処理
  my $cb = sub {
      my ($event, $cell) = @_;
      $self->send($cell);
  };
  # MyEventのreceiveイベントに登録する
  $event->on(receive => $cb);

reverse.pl(2)

  # WebSocketのメッセージ受信時の処理
  $self->on(message => sub {
    my ($self, $cell) = @_;
    # MyEventに送る
    $event->send($cell);
  });
  # WebSocket終了時の処理
  $self->on(finish => sub {
    my ($self, $code, $reason) = @_;
    # MyEventのreceiveイベント受け取りをやめる
    $event->unsubscribe(receive => $cb);
  });

reverse.pl(3)

    var ws = new WebSocket('<%= url_for('share')->to_abs %>');
    ws.onmessage = function(event) {
      var cellid = '#' + event.data;
      $(cellid).toggleClass('on');
    };
    $('.cell').click(function() {
      var $cell = $(this);
      ws.send($cell.attr('id'));
    });

EventSource

  • Server-Sent EventsというHTML5からの新機能
  • Webサーバからクライアントへのイベントやデータのプッシュ
  • やってることはLong polling
  • コネクション切れたらEventSourceのオブジェクトが生きているうちは勝手に再接続する
  • Mojolicious::Controllerにはコンテンツを細切れで送るためのメソッドがある

デモ:logtail.pl

  • ログファイルをtail -Fしてブラウザに逐次送信するデモ

logtail.pl(1)

  # コネクションのタイムアウトを長めにする
  Mojo::IOLoop->stream($self->tx->connection)->timeout(300);
  # レスポンスのContent-Typeをtext/event-streamにする
  $self->res->headers->content_type('text/event-stream');
  # ログファイルをtailで監視する
  my $pid = open(my $fh, "-|", "tail -q -n 0 -F test.log");
  if (!$pid) {
    # 開けなかったらエラーを返す
    $self->write("data: can't open test.log.\n\n");
    return;
  }

logtail.pl(2)

  # ログファイルのハンドルを非同期で処理する
  my $stream = Mojo::IOLoop::Stream->new($fh);
  # ログファイル読み込み時の処理
  $stream->on(read => sub {
    my ($stream, $bytes) = @_;
    # すべての行を送信する
    foreach my $line (split(/\n/, $bytes)) {
      $self->write("data: $line\n\n");
    }
  });

logtail.pl(3)

  # コネクション切断時
  $self->on(finish => sub {
    my $self = shift;
    # tailを殺す
    kill 'QUIT', $pid;
  });
  # ログファイルの処理を開始する
  $stream->start;

logtail.pl(4)

    var source = new EventSource('<%= url_for 'events' %>');
    source.onmessage = function(event) {
      $('#log').append(event.data + '<br/>');
    };

まとめ

  • Mojolicious使うとわりとお手軽にリアルタイムWebを体験できる
  • Mojolicious::Liteでちょっと書いて動かしながら試すとわかりやすい
  • Mojoliciousのドキュメントは充実してきているけど、細かい部分はソースを見たほうが早い
  • 今回の発表資料はhttps://github.com/niratama/chibapm3に置いておきます
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment