Download - Real-Time Python Web: Gevent and Socket.io
- Getting started with Gevent
- AJAX, push, WebSockets, wha?
- ZeroMQ for fun and multiprocessing
- Putting it all together
© 2011Geeknet Inc
A (very) Brief Survey of Python Asynchronous Programming
AsynCore In stdlib, used for stdlib SMTP server Nobody cares about it anymore
Twisted Large community, vast amounts of code Callbacks hurt my brain
Stackless Cool, cooperative multithreading Needs a custom Python
Event-based green threads Like stackless, but in regular Python Know when you yield
© 2011Geeknet Inc
Let’s Go Green: Async that Doesn’t Hurt Your Brain
Greenlets: Cooperative, lightweight threads
Very forgiving – mutexes rarely needed
Use it for IO!
© 2011Geeknet Inc
Gevent: Greenlets
Spawn helpers spawn(my_python_function, *args, **kwargs) Also spawn_later(), spawn_link(), etc.
Greenlet class Like threads but cooperative Useful properties: .get(), .join(), .kill(), .link()
Timeouts Timeout(seconds, exception).start()
Pools: for limiting concurrency, use Pool.spawn
© 2011Geeknet Inc
Gevent: Communication
Event set() clear() wait()
Queue Modeled after Queue.Queue .get() .put() __iter__() PriorityQueue, LifoQueue, JoinableQueue
© 2011Geeknet Inc
Gevent: Networking
“Green” versions of sockets, select(), ssl, and dns Quick and dirty:
import gevent.monkey
gevent.monkey.patch_all()
© 2011Geeknet Inc
Gevent: Servers
Simple callback interface Creates one greenlet per connection (but remember,
that’s OK!)
def handle(socket, address):
print 'new connection!’
server = StreamServer(
('127.0.0.1', 1234), handle)
# creates a new server
server.start()
# start accepting new connections
© 2011Geeknet Inc
Gevent: WSGI
gevent.wsgi Fast (~4k requests/s) No streaming, pipelining, or ssl
gevent.pywsgi: Full featured Slower (“only” 3k requests/s)
from gevent import pywsgi
def hello_world(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
yield '<b>Hello world</b>’
server = pywsgi.WSGIServer(
('0.0.0.0', 8080), hello_world)
server.serve_forever()
© 2011Geeknet Inc
- Getting started with Gevent
- AJAX, push, WebSockets, wha?
- ZeroMQ for fun and multiprocessing
- Putting it all together
© 2011Geeknet Inc
What is the real-time web? No page refreshes
Server push
Examples: chat, realtime analytics, …
Implementation Flash (eww…) Polling (2x eww…) Long polling (wow – that’s clever :-/ ) HTML5 WebSockets (Super-easy! Awesome! Security
vulnerabilities! Immature spec!)
© 2011Geeknet Inc
SocketIO to the Rescue
“Socket.IO aims to make realtime apps possible in every
browser and mobile device, blurring the differences
between the different transport mechanisms.”
© 2011Geeknet Inc
Socket.io Example
<script src="/socket.io/socket.io.js"></script><script> var socket = io.connect('http://localhost'); socket.on('news', function (data) { console.log(data); socket.emit('my other event’, { my:'data' }); });</script>
© 2011Geeknet Inc
gevent_socketio
def hello_world(environ, start_response): if not environ['PATH_INFO'].startswith( '/socket.io'): return serve_file(environ, start_response) socketio = environ['socketio'] while True: socketio.send('Hello, world') gevent.sleep(2)
© 2011Geeknet Inc
- Getting started with Gevent
- AJAX, push, WebSockets, wha?
- ZeroMQ for fun and multiprocessing
- Putting it all together
© 2011Geeknet Inc
ZeroMQ Overview
C library with Python bindings
ZMQ “sockets” are message based, delivery is via a dedicated communication thread
ZMQ transports (tcp, inproc, unix, multicast)
ZMQ socket types REQ/RES PUSH/PULL PUB/SUB …
© 2011Geeknet Inc
pyzmq and gevent_zmq
pyzmq works great for threading
gevent_zmq is necessary for gevent (single-threaded)
Be careful when forking or otherwise using multiprocessing!
Gevent has a global “hub” of greenlets ZeroMQ has a global thread & “context” for communication Best to wait till done forking before initializing ZeroMQ
© 2011Geeknet Inc
ZeroMQ: bind/connect and pub/sub
from gevent_zeromq import zmq
context = zmq.Context()
sock_queue = context.socket(zmq.PUB)
sock_queue.bind('inproc://chat')
zmq_sock = context.socket(zmq.SUB)
zmq_sock.setsockopt(zmq.SUBSCRIBE, "")
zmq_sock.connect('inproc://chat')
© 2011Geeknet Inc
- Getting started with Gevent
- AJAX, push, WebSockets, wha?
- ZeroMQ for fun and multiprocessing
- Putting it all together
© 2011Geeknet Inc
WebChat: Design
IncomingGreenlet
ZMQ send
OutgoingGreenlet
Socket.io
ZMQ recv
JSON Messages JSON Messages
Socket.io
© 2011Geeknet Inc
WebChat: HTML
<h1>Socket.io Chatterbox</h1><div id="status" style="border:1px solid black;"> Disconnected</div><form> <input id="input" style="width: 35em;"></form><div id="data" style="border:1px solid black;"></div><script src="/js/jquery.min.js"></script><script src="/js/socket.io.js"></script><script src="/js/test.js"></script>
© 2011Geeknet Inc
WebChat: JavascriptSetup
(function() { // Create and connect socket var socket = new io.Socket('localhost'); socket.connect(); // Socket status var $status = $('#status'); socket.on('connect', function() { $status.html('<b>Connected: ' + socket.transport.type + '</b>'); }); socket.on('error', function() { $status.html('<b>Error</b>'); }); socket.on('disconnect', function() { $status.html('<b>Closed</b>'); });
© 2011Geeknet Inc
WebChat: JavascriptCommunication
// Send data to the server var $form = $('form'); var $input = $('#input'); $form.bind('submit', function() { socket.send($input.val()); $input.val(''); return false; }); // Get data back from the server var $data = $('#data'); socket.on('message', function(msg) { msg = $.parseJSON(msg); var u = msg.u || 'SYSTEM’; $data.prepend($( '<em>' + u + '</em>: ' + msg.m + '<br/>')); });})();
© 2011Geeknet Inc
WebChat: Server
def chat(environ, start_response): if not environ['PATH_INFO'].startswith( '/socket.io): return serve_file(environ, start_response) socketio = environ['socketio'] #... handle auth ... zmq_sock = context.socket(zmq.SUB) zmq_sock.setsockopt(zmq.SUBSCRIBE, "") zmq_sock.connect('inproc://chat') greenlets = [ gevent.spawn(incoming, uname, socketio), gevent.spawn(outgoing, zmq_sock, socketio) ] gevent.joinall(greenlets)
© 2011Geeknet Inc
WebChat: Greenlets
def incoming(uname, socketio): while True: for part in socketio.recv(): sock_queue.send(json.dumps(dict( u=uname, m=part)))
def outgoing(zmq_sock, socketio): while True: socketio.send(zmq_sock.recv())
© 2011Geeknet Inc
Get the Code!
Socket.iohttp://socket.ioMIT License
Chatterboxhttp://sf.net/u/rick446/
pygothamApache License
ZeroMQhttp://www.zeromq.org
LGPL License
Geventhttp://gevent.org
MIT License