0

Hi I am having issues getting django channels working in production. I am running

gunicorn --bind 0.0.0.0:8000 project_name.asgi

to test that my ASGI setup is working correctly (This all works on my local machine which is super odd. Maybe I am missing something):

This is the error I keep getting :( I have tried everything online but nothing is working:

Traceback (most recent call last):
  File "/home/quilt/quilt_env/lib/python3.10/site-packages/gunicorn/workers/sync.py", line 136, in handle
    self.handle_request(listener, req, client, addr)
  File "/home/quilt/quilt_env/lib/python3.10/site-packages/gunicorn/workers/sync.py", line 179, in handle_request
    respiter = self.wsgi(environ, resp.start_response)
TypeError: ProtocolTypeRouter.__call__() missing 1 required positional argument: 'send'

my requirement file has the following:

channels==4.0.0
channels-redis==4.0.0
daphne==4.0.0
Django==4.1.5
billiard==4.1.0
asgiref==3.6.0

This is my settings file:

ASGI_APPLICATION = 'project_name.asgi.application'

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": ["127.0.0.1", "6379")],
        },
    },
}

This is my asgi.py setup:

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project_name.settings')
django.setup()

from channels.security.websocket import AllowedHostsOriginValidator
from channels.routing import ProtocolTypeRouter, URLRouter
from django_channels_jwt_auth_middleware.auth import JWTAuthMiddlewareStack


import notifications.routing

application = ProtocolTypeRouter({
  'http': get_asgi_application(),
  'websocket': AllowedHostsOriginValidator(
      JWTAuthMiddlewareStack(
          URLRouter(
            notifications.routing.websocket_urlpatterns
          ),
      )
  )
})

This is my routing in notification app:

from django.urls import path
from notifications import consumers

websocket_urlpatterns = [
    path("ws/", consumers.NotificationConsumer.as_asgi()),
]

And then this is my consumer.py file:

import json

from asgiref.sync import sync_to_async
from channels.exceptions import StopConsumer

from channels.generic.websocket import (
    AsyncWebsocketConsumer,
)

from notifications.models import Notifications


class NotificationConsumer(AsyncWebsocketConsumer):
    """
    This consumer is used to send notifications.
    """

    def __init__(self, *args, **kwargs):
        super().__init__(args, kwargs)
        self.group_name = ""
        self.user = None

    @sync_to_async
    def get_all_notifications(self):
        return list(
            Notifications.objects.filter(user=self.user, is_read=False).order_by(
                "-created_date"
            )[0:10]
        )

    async def connect(self):
        self.group_name = "notification"
        self.user = self.scope["user"]
        if not self.user.is_authenticated:
            return
        print("Connected!")

        await self.channel_layer.group_add(self.group_name, self.channel_name)
        await self.accept()

        for notification in await self.get_all_notifications():
            await self.send(
                text_data=json.dumps(
                    {
                        "id": notification.id,
                        "type_value": notification.type_value(),
                        "data": notification.serialized_notification_data_json(),
                    }
                )
            )

    async def disconnect(self, event):
        print("closed connection")
        print("Close code = ", event)
        await self.close()
        raise StopConsumer

    async def receive_json(self, content, **kwargs):
        print(content)
        return super().receive(text_data=json.dumps(content), **kwargs)

    async def notification_send(self, event):
        await self.send(
            text_data=json.dumps({
                "id": event["message"]["id"],
                "type_value": event["message"]["type_value"],
                "data": event["message"]["data"],
            })
        )
Darren-Jaen
  • 155
  • 1
  • 10
  • Does this code work on your local server? – Kyvex Jan 24 '23 at 16:33
  • Weirdly yes it works on my local server. Which is why this is super odd. When googling the error it says to add .as_asgi() in routing. Which I have done. – Darren-Jaen Jan 24 '23 at 16:34
  • IMO, I'd add a name to your route in your `routing.py` because if you plan on making more `WebSockets`. Ex. `ws/notification/`. Also maybe try removing `AllowedHostsOriginValidator` and `JWTAuthMiddlewareStack` from your `asgi.py` file and wrap your `URLRouter` in `AuthMiddleWareStack` from `from channels.auth import AuthMiddlewareStack` – Kyvex Jan 24 '23 at 16:43
  • Still getting the ProtocolTypeRouter.__call__() missing 1 required positional argument: 'send' error in prod. Works in local tho – Darren-Jaen Jan 24 '23 at 16:49
  • Are you using Nginx, Supervisor, and Gunicorn? – Kyvex Jan 24 '23 at 16:52
  • Gunicorn: testing this running: gunicorn --bind 0.0.0.0:8000 quilt_backend.asgi – Darren-Jaen Jan 24 '23 at 16:54
  • Running gunicorn --bind 0.0.0.0:8000 quilt_backend.wsgi (with wsgi) it works. – Darren-Jaen Jan 24 '23 at 16:54
  • 1
    AHHH I changed my /etc/systemd/system/gunicorn.service ExecStart to the following and solved my issue: ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \ --access-logfile - \ -k uvicorn.workers.UvicornWorker \ --workers 3 \ --bind unix:/run/gunicorn.sock \ myproject.asgi:application – Darren-Jaen Jan 24 '23 at 17:24
  • 1
    Using uvicorn.workers.UvicornWorker instead of just gunicorn – Darren-Jaen Jan 24 '23 at 17:24

0 Answers0