Brief Introduction to Worker in Gunicorn Python

Python application deployed in production need to handle a lot of incoming requests so we use Gunicorn to deploy WSGI application, this blog will help understand its different worker type.

Shaharyar
3 min readOct 5, 2020
Deployment using Gunicorn

Web applications that process the incoming HTTP request concurrently are more efficient than those that process one request at a time so it is recommended to use web servers that support concurrent request processing whenever developing and running production services.

Flask and Django come up with in built web server but they are not efficient as they process one request at a time act as blocking server . Gunicorn is a pure-Python HTTP server for WSGI applications it allow us to process multiple request concurrently.

Gunicorn is based on pre fork worker model that is single master process manages all the set fork processes (worker) master never knows about the individual client all the request and response are handle by worker processes.

Worker Type in Gunicorn:

There are different type of Worker in Gunicorn which can be classified into broadly three categories

  1. Per request process: This type of worker is design to handle the one request at time and act is blocking server
  2. Per request a thread: This type of worker work on the principal of handling request at thread level each request is handled by the thread spawn by fork process worker
  3. Async IO: This type of worker would handle multiple requests at one process with async IO example gevent, tornodo

Per request Process:

In this type of model on startup the django create various fork process refer to us worker and each worker is responsible for handling incoming request so whenever all the worker are busy in processing the requests other incoming request get blocked so maximum number of request that can be handling is equal to fork processes or worker

Normal Convention is to have 2*(number of processer) + 1 worker so it is evident from this that for 4 core CPU number of simultaneous request that can be handled concurrently are 9

This is default type of worker in Gunicorn and refer to as sync worker

Per request a thread:

when type of worker is set in Gunicorn then incoming request are handled at the thread level on startup the django fork multiple child process or worker when new request comes the worker create the new thread to handle that request so single worker can handle multiple request allowing more concurrency than the sync type and maximum concurrent request can be handled is number of worker times number of threads that can be forked

example is gthread worker

Async IO:

In this type worker class is set to gevent , gunicorn would handle multiple requests at one process. gevent worker class is built on top of libevent/libuv and greenlets . Javascript developer must be fimilar with words libuv which provides the event loop and the non blocking i/o capabilities to nodejs similarly gevent worker class implement libuv for proving non blocking functionality and multiple request can be handled by them without being blocked

Conclusion

Many of you might think which worker type to use for production deployment so let us understand there benefit and use case

  1. when you want that in error in one request only effect that request then it would beneficial to have the sync worker type as we have allocated one processor per request
  2. To increase the concurrency of the server we can increase the number of worker using sync worker and we can allocate each incoming request separate worker but downside is processes are more heavier and our system can’t keep up with the increasing work load by increasing the worker so one approach is to attach light weight thread to each worker and handle each incoming request at the thread level with this approach we can increase the parallelism to great extent
  3. Although the per request a thread seems great idea but still thread consume memory so creating large number of thread would not be great idea so better approach will be to use gevent worker-type which help us have functionality of threads without actually creating it

In upcoming blog I will discuss each of this in detail with working example

--

--