I have a question that might have a very simple answer.
Everywhere I look it says that the Django development server (manage.py runserver) is multithreaded (https://docs.djangoproject.com/en/3.2/ref/django-admin/) but this is not what I am experiencing.
DISCLAIMER: I know there are other ways to achieve this but I find this solution to be interesting and I cannot understand why it does not work.
I want to create one endpoint in my API that uses another endpoint's response to generate a report, the Views are set up as follows:
from rest_framework.views import APIView
from rest_framework.response import Response
from asgiref.sync import async_to_sync
class View1(APIView):
def get(self, request, *args, **kwargs):
response_dict = {"message": "Success!"}
return Response(response_dict)
class View2(APIView):
def get(self, request, *args, **kwargs):
client = Session()
response = self.get_response(client)
if response.get("Message") == "Success!":
return Response("Success!")
return Response("Failed!")
@async_to_sync
async def get_response(self, client):
return await client.get("http://localhost:8000/api/view1"#).json()
Now in my eyes this code looks like it should work because the request to View2 should be picked up by a first worker and the request that View2 is making to View1 should be picked up by a different worker, so that when the request to View1 is completed the request to View2 can be completed.
What I am seeing, using asgiref==3.4.1, Django==3.2.8, and djangorestframework==3.12.4 is that the request for View2 gets stuck just at the line where it makes the request to View1 and I would love to understand why that is the case.
Everywhere I look it says that the Django development server (manage.py runserver) is multithreaded
If you are talking about:
--nothreading
Disables use of threading in the development server. The server is multithreaded by default.
This option prevents the addition of socketserver.ThreadingMixIn to the wsgiref.simple_server and affects how the server handles network connections. The simple_server
is, as the name suggests, a simple WSGI server that comes with Python and is used by Django to run its development server (via python manage.py runserver
). On top of that, Django uses a separate thread for its async_to_sync
and sync_to_async
magic.
Now, to answer your question:
In order to handle multiple blocking requests at the same time, you need multiple workers (think of it as multiple servers plus a load balancer). And while running multiple workers usually implies multithreading or multiprocessing, a webserver being multithreaded doesn't automatically imply multiple workers processing requests.
For your specific code example, I would recommend converting everything to use async
. While you should be able to run it fine with the vanilla runserver
. If you pip install channels["daphne"]
and add "channels"
to the INSTALLED_APPS
it will replace runserver
command with its own that uses Daphne (ASGI) instead of simple WSGI server.