Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Eventlet, ZeroMQ, and You

Eventlet, ZeroMQ, and You

A talk I gave at PyCon Ukraine 2011.

Andrew Godwin

October 22, 2011
Tweet

More Decks by Andrew Godwin

Other Decks in Programming

Transcript

  1. Eventlet, ZeroMQ & You
    Andrew Godwin
    @andrewgodwin

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  5. 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)

    View Slide

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

    View Slide

  7. 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'

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  14. 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)

    View Slide

  15. 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])

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. Version Three: Fresh, Separate
    Codenamed ”Mantrid”

    View Slide

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

    View Slide

  22. 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,
    )

    View Slide

  23. 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
    )

    View Slide

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

    View Slide

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

    View Slide

  26. REQ / REP
    PUB / SUB
    PUSH / PULL

    View Slide

  27. 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()

    View Slide

  28. 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)

    View Slide

  29. 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!')

    View Slide

  30. 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()

    View Slide

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

    View Slide

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

    View Slide

  33. 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()

    View Slide

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

    View Slide

  35. 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])

    View Slide

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

    View Slide

  37. Logging clients
    They're the things that make logs

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide