まらんさんのチラ裏

その日暮らしのおじさん

gunicorn + flask + gevent + websocket

エコーバックするだけのアプリです。
websocket といえば node.js みたいな空気が嫌だったので python でもやれるよってことを書きたかっただけっていう。
apache で公開しようとするとなんか普通にやっても無理っぽいし、じゃあ gunicorn 使えばいいじゃんってことでやってみたんだけど意外とこういう情報がまとまっていなかったので折角だし。

gunicorn, libevent は既に入っているものとして書きます。
python 側は、flask, gevent, gevent-websocket が必要になるので pip とかで install してください。

ファイル構成

├── gunicorn.py
├── index.py
├── runserver.sh
└── templates
    └── index.html

index.py

#!/bin/env python
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from flask import Flask, request, render_template


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

ctx = {}
ctx['port'] = 8080

@app.route('/')
def index():
  return render_template('index.html', ctx=ctx)

@app.route('/api')
def api():
  if request.environ.get('wsgi.websocket'):
    ws = request.environ['wsgi.websocket']
    while True:
      message = ws.receive()
      if message is None:
        break
      ws.send(message)
  return

if __name__ == '__main__':
  http_server = WSGIServer(('',ctx['port']), app, handler_class=WebSocketHandler)
  http_server.serve_forever()

templates/index.html

<!DOCTYPE HTML>
<html>
<head>
  <title>Flask/Gevent WebSocket Test</title>
  <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
  <script type="text/javascript" charset="utf-8">
      $(document).ready(function(){
          $('form').submit(function(event){
              ws.send($('#data').val())
              return false;
          });
          if ("WebSocket" in window) {
              ws = new WebSocket("ws://" + document.domain + ":" + {{ ctx.port }} + "/api");
              console.log("WebSocket is supported by your Browser!");

              // Let us open a web socket
              ws.onopen = function() {
                // Web Socket is connected.
                console.log("Message is sent...");
              };
              ws.onmessage = function(msg) {
                  $("#log").append("<p>"+msg.data+"</p>")
              };
              ws.onerror = function() {
                // websocket is closed.
                console.log("Error ...");
              };
              ws.onclose = function() {
                // websocket is closed.
                console.log("Connection is closed...");
              };
          } else {
              alert("WebSocket not supported");
          }
      });
  </script>
</head>
<body>
  <p>gunicorn + flask + gevent + websocket</p>
  <h1>Send:</h1>
  <form method='POST' action='#'>
    <textarea name='data' id="data"></textarea>
    <div><input type='submit'></div>
  </form>
  <h1>Receive:</h1>
  <div id="log"></div>
</body>
</html>

gunicorn.py

bind = '127.0.0.1:8080'
workers = 5
backlog = 2048
worker_class = 'gevent'
debug = True
daemon = True
pidfile = 'gunicorn.pid'
logfile = 'gunicorn.log'

runserver.sh

pkill -f 'gunicorn'
gunicorn -c gunicorn.py -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" index:app

動かし方

chmod +x runserver.sh
./runserver.sh

とかして、 http://127.0.0.1:8080/ にアクセスすれば動きます。