pythonwebserverwsgigeventgreenlets

Gevent Pywsgi Server - Multiprocessing?


The following code (taken from here: https://bitbucket.org/denis/gevent/src/6c710e8ae58b/examples/wsgiserver_ssl.py) implements an extremely fast greenlet powered wsgi webserver:

#!/usr/bin/python
"""Secure WSGI server example based on gevent.pywsgi"""

from gevent import pywsgi


def hello_world(env, start_response):
    if env['PATH_INFO'] == '/':
        start_response('200 OK', [('Content-Type', 'text/html')])
        return ["<b>hello world</b>"]
    else:
        start_response('404 Not Found', [('Content-Type', 'text/html')])
        return ['<h1>Not Found</h1>']

print 'Serving on https://127.0.0.1:8443'
server = pywsgi.WSGIServer(('0.0.0.0', 8443), hello_world, keyfile='server.key', certfile='server.crt')
# to start the server asynchronously, call server.start()
# we use blocking serve_forever() here because we have no other jobs
server.serve_forever()

However, this only runs on one core. How would you modify this to take advantage of multiple processes? Not looking for an answer that involves gunicorn, looking for something simpler.

HINT

Here is a code sample using gevent and multiprocessing, but I still can't figure out how to make this work with WSGI (taken from https://gist.github.com/1169975):

import sys
from gevent import server
from gevent.baseserver import _tcp_listener
from gevent.monkey import patch_all; patch_all()
from multiprocessing import Process, current_process, cpu_count


def note(format, *args):
    sys.stderr.write('[%s]\t%s\n' % (current_process().name, format%args))

def echo(socket, address):
    print 'New connection from %s:%s' % address
    fileobj = socket.makefile()
    fileobj.write('Welcome to the echo server! Type quit to exit.\r\n')
    fileobj.write('In %s\r\n' % current_process().name)
    fileobj.flush()
    while True:
        line = fileobj.readline()
        if not line:
            print "client disconnected"
            break
        if line.strip().lower() == 'quit':
            print "client quit"
            break
        fileobj.write(current_process().name + '\t' + line)
        fileobj.flush()
        print "echoed", repr(line)

listener = _tcp_listener(('127.0.0.1', 8001))

def serve_forever(listener):
    note('starting server')
    server.StreamServer(listener, echo).serve_forever()

number_of_processes = 5
print 'Starting %s processes' % number_of_processes
for i in range(number_of_processes):
    Process(target=serve_forever, args=(listener,)).start()

serve_forever(listener)

Solution

  • So why not to make this in the same way? https://gist.github.com/1217855

    import sys
    from gevent import server
    from gevent.baseserver import _tcp_listener
    from gevent import pywsgi
    from gevent.monkey import patch_all; patch_all()
    from multiprocessing import Process, current_process, cpu_count
    
    def hello_world(env, start_response):
        if env['PATH_INFO'] == '/':
            start_response('200 OK', [('Content-Type', 'text/html')])
            return ["<b>hello world</b>"]
        else:
            start_response('404 Not Found', [('Content-Type', 'text/html')])
            return ['<h1>Not Found</h1>']
    
    listener = _tcp_listener(('127.0.0.1', 8001))
    
    def serve_forever(listener):
        pywsgi.WSGIServer(listener, hello_world).serve_forever()
    
    number_of_processes = 5
    print 'Starting %s processes' % number_of_processes
    for i in range(number_of_processes):
        Process(target=serve_forever, args=(listener,)).start()
    
    serve_forever(listener)