When it’s ready.

出来るまで出来ない

リアルタイムWeb 勉強会に参加した

Greeさんの最高です!会場ありがとうございました。

node.jsのハカソンをやって、リアルタイムWeb関係のLTをやるという進行の会でした。node.jsはwktkですが、手が出せませんでした。
そのかわりFlaskとみんな大好きなmeinheldを使ったWebSocketアプリ作ってみました。

リアルタイムで出来たら嬉しいと思うことにサーバーの状態監視があります。sshが使えない環境でも、ブラウザ一つでtopがリアルタイムに更新されたら嬉しいなぁと思っていたのでこれを気にやってみたら意外と簡単に出来ました。

ファイル構成

ws_test/.

-- __init__.py
-- monitor.py
-- templates
-- websocket.html

`-- websocket_top.py

各ソース

websocket_top.py

from flask import Flask, render_template, request
from meinheld import server, middleware
from multiprocessing import Process
from subprocess import call

import commands, time

SECRET_KEY = 'development key'
DEBUG=True

app = Flask(__name__)
app.config.from_object(__name__)

participants = set()

@app.route('/')
def index():
  return render_template('websocket.html')

@app.route('/top')
def top():
  ws = request.environ.get('wsgi.websocket')
  participants.add(ws)
  try:
    while True:
      print "ws.wait()..."
      m = ws.wait()
      print "recv msg %s" % m
      if m is None:
        break
      for p in participants:
        print "send message %s" % m
        if m.isdigit():
          mp = Process(target=send_top, args=(p, int(m)))
          mp.start()
  finally:
    participants.remove(ws)
  return ""

def send_top(obj,  count=5):
  for i in range(count):
    str_top = ["%s"%x for x in commands.getoutput("top -b -n1").split('\n')[:10]]
    res_str = "<pre>" + '\n'.join(str_top) + '</pre>'
    obj.send(res_str)
        
if __name__ == "__main__":
  server.listen(("0.0.0.0", 5000))
  server.run(middleware.WebSocketMiddleware(app))


websocket.html

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script>
window.onload = function() {
  var data = {};
  var s = new WebSocket("ws://main.atusi.me:5000/top");
  s.onopen = function() {
    s.send('New participant joined');
  };
  s.onmessage = function(e) {
    $("#top-stat").html("<div>" + e.data + "</div>");
  };
  s.onclose = function(e) {
    $("#top-stat").html("<div>" + 'close connection' + "</div>");
  }
  $('#connect').submit(function (evt) {
      var line = $('#connect [type=text]').val()
      $('#connect [type=text]').val(line)
      s.send(line);
      return false;
      });

  $('#disconnect').submit(function (evt) {
      s.onclose();
      return false;
      });
};
</script>

<style>
  #chat {
    font:12px "DejaVu Sans Mono","Bitstream Vera Sans Mono",monospace;
  }
</style>
</head>

<body>
  <h3>top</h3>
  <form id="connect">
    <input type="text" value=3 />
    <input type="submit" value='open' />
  </form>

  <form id="disconnect">
    <input type="submit" value='close' />
  </form>

  <div id="top-stat" style="width: 60em; height: 20em; overflow:auto;">
  </div>
</body>
</html>

はまりどこ

Pythonからtopの内容が抜けない

callでtop呼んでしまうと、標準出力されてしまうし返ってこないので困ります。
topに関しては、top -b -n1 オプション付けることで一回だけ実行して返ってくるようになったので解決>thx i386氏
callはもともと欲しい機能がついてないぽいので(2.7からそういう事が出来るようになってる)、commands.getoutput を使うことで標準出力部分を取得することが出来ました。

複数回topすると、他の閲覧者の人がブロックされてしまう

一回だけで実行すると、複数の閲覧者がいた場合にそれぞれのブラウザで交互に更新が行われて、それポイ動作をするんだけど、複数回のtopを実行すると一人に対して表示が更新され指定の回数が終了するまで、その他の人のブラウザは一切更新されません。これだと同時に複数ヶ所からのモニターが出来ない為NG。
どのように解決していいか未だベストがわかりませんが、今回は、wsオブジェクトをそのまま引数で渡す形でmultiprocessingで別プロセス化してブロッキングを回避しました。フォームに繰り返し回数を数値入力するとその回数だけtopを繰り返します。
もっと正しいやり方があるとおもうんだけど思いつかないです、プリーズヘルプミィですぉ
JSの部分なんだけど、WebSocketのcloseの仕方が分からない、どうすればいいんだろ?

リアルタイムについての妄想

”リアルタイムなんとか”と言うのを最近よく見かけるようになったけど、リアルタイムの定義ってなんだろう?とちょっと考えさせられる。低遅延とか、固定遅延とか、そういう意味で”リアルタイム”という言葉がWeb界隈で使われている気がする。現実時間はNot離散なので、PCで扱おうとすると何がしか離散させなくてはいけないとなると、その分解能をどれくらいにするのかに興味が湧く?映画だったら24F/secだし、テレビだったら60i/secだし、音楽ファイルだったら44.1KHzだし、CPUとかだと2.5GHzとか?多分人間の視覚的には250Update/Sec以上は認知不能だと思うのでそれくらいの分解能を基準してみるのはどうなんだろう?でも実際のところ、 画面のデータはリアルタイムで更新(250Update/Sec)しているつもりでも、液晶モニターがそもそも60Hzだとあまり意味がないし、ネットワークの行って来いで25msもかかってたら40回/secなわけ(非同期にすればなんぼでも行けるか?)で250っていう数字はあまり現実的じゃないのかな?最近の地デジテレビで4倍速とかは240F/sec相当なのでこれのちょっと上くらいの数字でPCリアルタイムの基準数字を作ったほうがいいかと思うんだけど、どうなんだろう?

とか、妄想させられた勉強会でした。

早くだれかタキオンを発見してください。