Python servers (e.g. Gunicorn) nowadays I guess mostly implement the WSGI protocol spec. WSGI was originally motivated by the need to consolidate a growing list of Python web frameworks, kinda similar to Java’s servlet API, and apparently succeeded in its goals.
WSGI flow:
(1) client sends HTTP requests (does it have to be HTTP? Not sure) over the
internet; (2) for each request, server calls app(env, start_response)
; (3)
Web application responds by calling start_response(status, headers, exc=None)
and then yields response strings.
Running a simple WSGI server will yield a working response.
Luckily wsgiref
is a part of Python since 2.5 (also cf. Django).
The simplest server implementation:
from wsgiref.simple_server import make_server
def app(environ, start_response):
start_response('200 OK', [('Content-type', 'text/plain; charset=utf-8')])
yield 'Heyo'.encode('utf-8')
yield from (f'{k}:{v}\n'.encode("utf-8") for k, v in environ.items())
class PointOutVimMiddleware:
def __init__(self, app):
self.app = app
def __call__(self, env, start_response):
for part in self.app(env, start_response):
yield part + ('<---' if 'vim' in part else '')
app = PointOutVimMiddleware(app)
with make_server('', 8000, app) as httpd:
httpd.serve_forever()
Ta-daa, there is a WSGI webserver. Similar description in Django’s official docs.
For server internals, cf. server.py
.
The serve_forever
method of socketserver.py
does what you’d expect it to.
GUnicorn
Seems Green Unicorn is one of the more popular threading servers for Python.
From the code.
Worker class
loaded from config.
BaseApplication
calls
the worker-managing
Arbiter.
Arbiter spawns workers
while jobs are available.
Cf. its master loop,
which does periodic checkups
on worker health.
If no workers are available, master goes to sleep and waits on pipe input
(cf. wakeup)
Workers that exceed some predefined timeout threshold
are sent the kill signal and aborted.
Then the size of the currently active threadpool is adjusted.
E.g. spawn new or kill extra workers.
NOTE: seems weird they sort and pop from start of list.
Could possibly be extremely inefficient here.
In practice
I guess vanilla Python server implementations might use something like supervisor or systemd to manage and monitor, nginx to proxy, gunicorn as server, and then a framework such as Django for the actual business logic implementation.
That’s kind of what Django docs seem to advocate, anyway. OK cool.