Slide 1

Slide 1 text

Eventlet, ZeroMQ & You Andrew Godwin @andrewgodwin

Slide 2

Slide 2 text

Who am I? http://www.flickr.com/photos/maistora/3014414972

Slide 3

Slide 3 text

The Plan http://www.flickr.com/photos/snapsi42/2609025344/

Slide 4

Slide 4 text

Eventlet Asynchronous concurrency framework http://www.flickr.com/photos/pagedooley/2814895287/

Slide 5

Slide 5 text

import eventlet from eventlet.green import urllib2 urls = ['http://ep.io', 'http://t.co'] results = [] def fetch(url): results.append(urllib2.urlopen(url).read()) for url in urls: eventlet.spawn(fetch, url)

Slide 6

Slide 6 text

Cooperative Threading You have to yield control. http://www.flickr.com/photos/nickwheeleroz/3406931272/

Slide 7

Slide 7 text

import eventlet import time def thread1(): time.sleep(5) # Does not yield print 'five seconds a' eventlet.spawn(thread1) time.sleep(5) # Does not yield print 'five seconds b'

Slide 8

Slide 8 text

import eventlet import time def thread1(): eventlet.sleep(5) print 'five seconds a' eventlet.spawn(thread1) eventlet.sleep(5) print 'five seconds b'

Slide 9

Slide 9 text

Greening the World It's that or monkeypatching. http://www.flickr.com/photos/mightyboybrian/3457507731/

Slide 10

Slide 10 text

import urllib2 from eventlet.green import urllib2 import zmq from eventlet.green import zmq import socket from eventlet.green import socket

Slide 11

Slide 11 text

Nice Primitives Not to mention less race conditions. http://www.flickr.com/photos/mightyboybrian/3457507731/

Slide 12

Slide 12 text

from eventlet.timeout import Timeout try: with Timeout(30): do_stuff() except Timeout: abort_abort()

Slide 13

Slide 13 text

from eventlet.semaphore import Semaphore sem = Semaphore(1) with sem: use_resource()

Slide 14

Slide 14 text

from eventlet.queue import Queue q = Queue() def worker(): while True: print q.get() def producer(): while True: eventlet.sleep(1) q.put('lol') eventlet.spawn(worker) eventlet.spawn(producer)

Slide 15

Slide 15 text

from eventlet import GreenPool from eventlet.pools import Pool conns = Pool(create=make_db_connection) threads = GreenPool(10) def querier(id): with pool.item() as conn: conn.execute('... WHERE id = %s', [id]) pool.imap(querier, [1, 2, 3, 4, 5])

Slide 16

Slide 16 text

Experiences from production There's still race conditions and bad libraries. http://www.flickr.com/photos/iamdabe/4931554963/

Slide 17

Slide 17 text

Case Study: Epio Loadbalancer Now at version three. http://www.flickr.com/photos/jasohill/118616905/

Slide 18

Slide 18 text

Version One: HAProxy + reloader Occasionally slow, occasionally didn't reload.

Slide 19

Slide 19 text

Version Two: Custom Integrated Worked alright, but leaked sockets.

Slide 20

Slide 20 text

Version Three: Fresh, Separate Codenamed ”Mantrid”

Slide 21

Slide 21 text

address = ('0.0.0.0', 80) family = socket.AF_INET sock = eventlet.listen(address, family) eventlet.serve( sock, self.handle, concurrency = 10000, )

Slide 22

Slide 22 text

address = ('127.0.0.1', 8042) family = socket.AF_INET sock = eventlet.listen(address, family) management_app = ManagementApp(self) wsgi.server( sock, management_app.handle, )

Slide 23

Slide 23 text

class Spin(Action): def handle(self, sock, read_data, path, headers): while True: # Sleep first eventlet.sleep(self.check_interval) # Check for another action action = self.balancer.resolve_host(self.host) if not isinstance(action, Spin): return action.handle( sock, read_data, path, headers )

Slide 24

Slide 24 text

ZeroMQ I was too lazy to use an Ø. http://www.flickr.com/photos/andresrueda/2926348197/

Slide 25

Slide 25 text

Not a message queue More like 'better sockets'. http://www.flickr.com/photos/96dpi/3816391053/

Slide 26

Slide 26 text

REQ / REP PUB / SUB PUSH / PULL

Slide 27

Slide 27 text

from eventlet.green import zmq ctx = zmq.Context() sock = ctx.socket(zmq.REQ) sock.connect('tcp://127.0.0.1:1234') sock.connect('tcp://127.0.0.1:4321') sock.send('hi there') print sock.recv()

Slide 28

Slide 28 text

from eventlet.green import zmq ctx = zmq.Context() sock = ctx.socket(zmq.REP) sock.bind('tcp://0.0.0.0:1234') while True: message = sock.recv() sock.send('you said: %s' % message)

Slide 29

Slide 29 text

from eventlet.green import zmq ctx = zmq.Context() sock = ctx.socket(zmq.PUB) sock.connect('tcp://127.0.0.1:1234') sock.send('Hello, world!')

Slide 30

Slide 30 text

from eventlet.green import zmq ctx = zmq.Context() sock = ctx.socket(zmq.SUB) sock.bind('tcp://0.0.0.0:1234') while True: print sock.recv()

Slide 31

Slide 31 text

The restrictions It's not quite too good to be true. http://www.flickr.com/photos/maistora/3237164755/

Slide 32

Slide 32 text

Advanced ZeroMQ & Eventlet When one greenthread is not enough http://www.flickr.com/photos/maistora/3237164755/

Slide 33

Slide 33 text

sock = ctx.socket(zmq.REQ) sock.send('help me! you're my only hope!') try: with Timeout(30): response = sock.recv() deal_with(response) except Timeout: cleanup()

Slide 34

Slide 34 text

@zmq_loop(zmq.XREP, 8000) def handle_request(string): eventlet.sleep(10) return string * 2

Slide 35

Slide 35 text

try: parts = sock.recv_multipart(flags=zmq.NOBLOCK) except zmq.ZMQError, e: if e.errno == zmq.EAGAIN: eventlet.sleep(0.1) continue else: raise identities = parts[:parts.index("")] def call(identities, data): result = func(self, data) sock.send_multipart(identities + ["", result]) eventlet.spawn_n(call, identities, parts[-1])

Slide 36

Slide 36 text

Case Study: Epio Logging We really, really like data http://www.flickr.com/photos/jasohill/118616905/

Slide 37

Slide 37 text

Logging clients They're the things that make logs

Slide 38

Slide 38 text

Logging servers They're the things that save and store logs

Slide 39

Slide 39 text

# Servers zmq.PULL -> Recieves logs zmq.REP -> Lets website query for log data # Clients zmq.PUSH -> Sends logs, connected to all servers

Slide 40

Slide 40 text

Conclusions Where Andrew waffles for a bit. http://www.flickr.com/photos/oimax/108058706/

Slide 41

Slide 41 text

Thanks. Andrew Godwin @andrewgodwin http://aeracode.org