Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Django use one thread to process several requests in WSGI or Gunicorn?

As per the title, I am wondering if Django, when run via WSGI or Gunicorn, uses one thread to process several requests?

I know it is a bad practice to access the request from places not meant to be accessed from, but I still want to do it. (for good reasons in my opinion, such as accessing the current user and site in my custom template loader. Django template loader doesn't offer these two entities.)

Now I managed to get access to the user, etc. from everywhere using the following bit of code:

import threading

_thread_locals = threading.local()

def get_current_request():
    return getattr(_thread_locals, "request", None)

class RequestThreadMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        _thread_locals.request = request
        response = self.get_response(request)
        return response

My question/concern is: is it guaranteed that requests/users don't get mixed up in production? I am concerned the second user, for instance, gets identified as the first user because the thread is being used for my than one request. Or, the request gets overwritten by another request while the original request hasn't finished yet. Is that possible, or every request has its own local thread in WSGI and Gunicorn?

like image 680
Adam Silver Avatar asked Mar 15 '21 02:03

Adam Silver


2 Answers

Consider here Gunicorn as a web server. It has master and workers processes. Master process selects free worker process for handling http request. There are 3 type of workers:

  • synchronous (sync)
  • synchronous with threads (worker shares memory for running threads)
  • asynchronous (async)

Each sync worker without threads handles single request at a time, consider we are having 2 requests they either would be handled by 2 different workers (separate and isolated from each other python processes) or same worker but sequentially or parallely by one worker if it has more then one thread To run an app with a sync workers without threads run it as follows:

gunicorn --workers=3 main:app

To run an app with a sync workers and with a threads:

gunicorn --workers=3 --threads=2 --worker-class=gthread main:app 

In above example each of the worker could handle 2 requests at a time.

With async worker we would have request concurrency: each worker (python process) would process many requests at a time but by one process. To run gunicorn with async workers your should properly set worker class:

gunicorn --worker-class=gevent --worker-connections=1000 --workers=3 main:app

It could be fully quarantied that request don't get mixed if you choose sync workers without threads and it seems that async too, for sync workers with threads your should to implement thread synchronization where multiple threads could write simultaneously.

like image 94
Michael Ushakov Avatar answered Oct 04 '22 17:10

Michael Ushakov


In theory, Django uses asynchronous instances to process different requests. That is as the request from a client comes to I/O operation, it will send the I/O request (i.e. look up in database) and then store the current status and let another client process its request. Thus, you need not worry "thread safe" or "thread data safe" issue. Because there is always a master thread in Django that takes care of production threads.

To check if Django actually runs multiple threads at one time, install and use htop on your Linux machine.

like image 37
George Y Avatar answered Oct 04 '22 15:10

George Y