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

Gunicorn, more than a WSGI server

Gunicorn, more than a WSGI server

Gunicorn is known as a Python WSGI HTTP Server for UNIX. But today the WSGI specification shows its limits and people wants more. This talk will introduce the new Gunicorn released in January with a new IPC library usable in others Python programs to handle the concurrency and support HTTP2.

Gunicorn handles the concurrency using a pre-fork worker model: workers are spawned processes managed by a main process. Until now there is no dialog between workers and the master which can trigger some issues like the thundering herd. To solve it and prepare gunicorn to support new protocols like HTTP2, a new IPC library has been specifically written in pure python. Mostly inspired from imsg in OpenBSD (but also Mojo), this new library is entirely written in Python and can be used outside of Gunicorn.

This talk will present the following:

1) The design of Gunicorn 1, its pros and cons 2) Description of the new IPC library how you can use it in your own projects 3) The design of Gunicorn 2 and its usage of the new IPC library, how it can do more than simply WSGI and HTTP 1.1.

Benoit Chesneau

January 30, 2016
Tweet

More Decks by Benoit Chesneau

Other Decks in Technology

Transcript

  1. A WSGI SERVER ▸ PEP 3333 ▸ pre-fork worker model

    ▸ Python > 2.6x ▸ handle Django & Paste configuration ▸ support any concurrency framework
  2. ADDITIONS TO WSGI ▸ ALREADY_HANDLED: take control of the socket

    ▸ ‘gunicorn.socket’ environ ▸ ‘gunicorn.raw_uri’ environ ▸ `transfer-encoding: chunked`
  3. PREFORK WORKER MODEL ARBITER WORKER WORKER share socket WORKER listen

    handle signals supervise accept WSGI server handle signals
  4. SHARE NOTHING ▸ share only file descriptors ▸ Privilege separation

    ▸ Worker: simple process with a run function
  5. WORKER accept loop setup signals sockets configuration log parent pid

    tempfile class MyWorker(WorkerBase): def __init__(..): … def init_process(self): … def run(self): …
  6. LET IT CRASH ▸ run the application ▸ need to

    notify the arbiter ▸ tempfile updates ▸ handle application errors ▸ but should crash on errors most of the time
  7. DO MORE. BE SAFER. ▸ improve listening in the sync

    worker ▸ remove the tempfile ▸ prepare for the new protocols ▸ improve logging ▸ going further
  8. ACCEPT WHEN NEEDED ▸ place the listening sockets in an

    event loop ▸ on read, start to accept ▸ also have a timer
  9. GUNICORN.IMSG ▸ communicate between processes ▸ use UNIX socket ▸

    sendmsg/recvmsg + buffer ▸ 2 methods: send, receive ▸ zero copy memory
  10. child parent IMSG EXAMPLE fds =socket.socketpair() pid = os.fork() if

    pid != 0: # parent socket.close(fds[1]) imsg = IMSG(fds[0] return main(imsg) # child socket.close(fds[0]) var imsg = IMSG(fds[1]) return main(imsg)
  11. wait for a message send a message IMSG EXAMPLE main(imsg):

    imsg.send(“sometype”, “somemsg”), dispatch_msg(imsg) dispatch_msg(imsg): while True: msg = imsg.recv() if msg is None: sleep(1) continue print(msg.data)
  12. REMOVING TEMPFILE ▸ a socket pair is opened for each

    workers ▸ an imsg object is created for each ▸ imsg object are placed in an eventloop to read asynchronously ▸ maybe extracted from the code as a simple lib
  13. DETACHED MODE ▸ rework ALREADY_HANDLED ▸ socket control is given

    to app ▸ but still supervised ▸ simple API
  14. simple proc abstraction return a proc instance EXAMPLE def app(environ,

    start_response): ..
 start_response(status, headers) proc = MyProc(environ) return proc class MyProc(gunicorn.Proc): def status(self): return STATUS_ALIVE def terminate(self, Reason): ….
  15. IMPROVE LOGGING ▸ logging will now be handled in its

    own process ▸ log infos passed via imsg and dispatched by the arbiter ▸ fix privilege separation issues