What is a web server? Testdriven.io makes a colorful introduction to the concept. To paraphrase: it accepts requests from clients (like a browser) and serves a response, that's all it does. The responsibility of understanding what was requested and how to respond to the request belongs to the application. But there needs to be a connection between the server and this application. And this connection, this interface, is where wsgi (or asgi) come in.
wsgi - web server gateway interface - is a python standard described in PEP 3333. Lately there's been a surge in asgi - asynchronous server gateway interface - implementations. For now though I'd like to focus on the traditional wsgi, specifically using the popular gunicorn.
Interface - gunicorn
Gunicorn is the bridge (the interface), if you will, for python-based apps like Django (application) to communicate with the actual web server (e.g. nginx / caddy)
runserver management command is invoked in Django it mimics the interface just described locally. We don't need an elaborate setup of servers in this context since, at this juncture, we're more interested in understanding business logic and application design. Once the context shifts to the realm of containers and the cloud,
runserver becomes ill-advised and
gunicorn becomes a popular choice. Django's warning is explicit:
runserver: ... DO NOT USE THIS SERVER IN A PRODUCTION SETTING. It has not gone through security audits or performance tests. (And that’s how it’s gonna stay. We’re in the business of making web frameworks, not web servers, so improving this server to be able to handle a production environment is outside the scope of Django.)
Relating Interface, Server, and Application
This is how I presently understand the relationship:
--- title: How a click is processed --- flowchart LR subgraph server-side server(((nginx/caddy)))<--->python subgraph python gunicorn---django end end subgraph client-side browser<--http request-response--->server end
Gunicorn receives requests and processes it through workers. Its docs elaborate:
Gunicorn is based on the pre-fork worker model. This means that there is a central master process that manages a set of worker processes. The master never knows anything about individual clients. All requests and responses are handled completely by worker processes.
Concretizing this description, I have this visual, mental model:
--- title: Visualizing worker processes --- flowchart LR subgraph gunicorn-master-process subgraph django-app-worker-process-1 d1req(request: get endpoint '/about/') d1res(response: render html template) d1req--route request url to response view-->d1res end subgraph django-app-worker-process-2 d2req(request: search 'hello world') d2res(response: query db, show results) d2req--route request url to response view-->d2res end subgraph django-app-worker-process-3 d3req(request: user signup) d3res(response: send email, request confirmation) d3req--route request url to response view-->d3res end end nginx(((web server)))<--request-response managed by gunicorn--->django-app-worker-process-1 nginx(((web server)))<--request-response managed by gunicorn--->django-app-worker-process-2 nginx(((web server)))<--request-response managed by gunicorn--->django-app-worker-process-3
According to Django's docs: [runserver] uses the WSGI application object specified by the
WSGI_APPLICATION setting. And in the default
settings.py, we see that
WSGI_APPLICATION = "config.wsgi.application"
So in essence, when we run
runserver on a user-defined project named
python manage.py runserver
It translates to: "connect to config/wsgi.py" and then "use application defined in that file". This is what that file looks like:
import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") application = get_wsgi_application()
Relatedly, the gunicorn docs contains a specific section on how it integrates with Django:
Gunicorn will look for a WSGI callable named
applicationif not specified. So for a typical Django project, invoking Gunicorn would look like: