Gunicorn is a Python Wsgi HTTP server that runs only on Unix systems and comes from the Unicorn Project of Ruby. Gunicorn uses the Prefork master-worker model (in Gunicorn, master is called arbiter) and is able to work with various WSGI Web frameworks. Gunicorn’s documentation is fairly complete, and there are some Chinese translations, but it is recommended to read the English version.

The installation of Gunicorn is very simple, PIP install Guncorn. If the asynchronous worker model is used later, corresponding modules (such as Gevent) need to be installed.

With Gunicorn installed, let’s take a look at Gunicorn’s Hello World. The code is from gunicorn_app.py. Put the following code into gunicorn_app.py:

def app(environ, start_response) :
    data = b"Hello, World! \n"
    start_response("200 OK", [("Content-Type"."text/plain"), ("Content-Length".str(len(data)))])  
    return iter([data])
Copy the code

You can see that the app is a very standard WSGI application, then we launch Gunicorn: Gunicorn-w 2 Gunicorn_app :app. The output is as follows:

The figure above shows two important pieces of information: first, two workers are started, which is specified by “-w 2” (default: 1). Second, the working model of the worker is sync (default). The worker model will be described in detail later

And then in another terminal operation: ps – ef | grep python

It can be seen that worker process (PID: 19469, 19470) is a child process of master process (PID: 19464).

Curl 127.0.0.1:8000: “Hello, World!”

As mentioned earlier, gunicorn will only run on Unix (there is a patch for it to run on Windows), mainly because the source code uses unix-only modules and interfaces such as FCNTL, os.fork, etc. Pre-fork means that when Gunicorn starts, a specified number of worker processes (-W) will be forked in advance in the main process. Here is a brief introduction of the working process of Master Worker, and then the code will be introduced in detail.

Start gunicorn, first initialize the gunicorn. App. Base. The Application (or base class, such as the above from the command line starts, is wsgiApplication), initialization is most important in reading configuration, supporting documents or the command line. Then call application.run (), which looks like this:

def run(self) :
    try:
        Arbiter(self).run()
    except RuntimeError as e:
        print("\nError: %s\n" % e, file=sys.stderr)
        sys.stderr.flush()
        sys.exit(1)
Copy the code

Arbiter(self).run(), all the logic of the master process is run in the Arbiter class.

Arbiter first reads configuration items, such as the number of worker, worker working mode, address monitored, etc.; Then initialize the signal handler and set up the socket without listening. Next, fork out all worker processes; Finally, we enter the loop: processing signals in the signal queue, killing and restarting unresponsive child processes, and “sleeping” for a while if there is nothing else to do.

The worker process is even simpler, first reading the configuration, initializing the signal handler, and then entering the loop: processing the request on the listening port (where the WSGi app will be called) and reporting to the master that it is still alive. In addition, workers directly process signals when they occur, rather than putting them into the signal queue like master.

You can slightly change the app code, so that when the client request, the server can print the call stack, the following is the output on my machine, will be introduced to Arbiter and worker.

  • 0: FUNC:app(…)                  /home/xxx/gunicorn_app.py::16
  • 1: FUNC:handle_request(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/workers/sync. Py: : 176
  • 2: FUNC:handle(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/workers/sync. Py: : 135
  • 3: FUNC:accept(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/workers/sync. Py: : 30
  • 4: FUNC:run_for_one(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/workers/sync. Py: : 68
  • 5: FUNC:run(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/workers/sync. Py: : 124
  • 6: FUNC:init_process(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/workers/base. The py: : 132
  • 7: FUNC:spawn_worker(…) / usr/local/lib/python2.7 / dist – packages/gunicorn arbiter. The py: : 557
  • 8: FUNC:spawn_workers(…) / usr/local/lib/python2.7 / dist – packages/gunicorn arbiter. The py: : 590
  • 9: FUNC:manage_workers(…) / usr/local/lib/python2.7 / dist – packages/gunicorn arbiter. The py: : 524
  • 10: FUNC:run(…) / usr/local/lib/python2.7 / dist – packages/gunicorn arbiter. The py: : 189
  • 11: FUNC:run(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/app/base. The py: : 72
  • 12: FUNC:run(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/app/base. The py: : 192
  • 13: FUNC:run(…) / usr/local/lib/python2.7 / dist – packages/gunicorn/app/wsgiapp. Py: : 74
  • 14: FUNC:(…)             /usr/local/bin/gunicorn::11