Yom86
Yom86

Reputation: 196

Django channels times out with daphne and worker

I have a problem with django channels. My Django app was running perfectly with WSGI for HTTP requests. I tried to migrate to channels in order to allow websocket requests, and it turns out that after installing channels and running ASGI (daphne) and a worker, the server answers error 503 and the browser displays error 504 (time out) for the http requests that were previously working (admin page for example). I read all the tutorial I could find and I do not see what the problem can be. Moreover, if I run with "runserver", it works fine.

I have an Nginx in front of the app (on a separate server), working as proxy and loadbalancer. I use Django 1.9.5 with asgi-redis>=0.10.0, channels>=0.17.0 and daphne>=0.15.0. The wsgi.py and asgi.py files are in the same folder. Redis is working.

The command I was previously using with WSGI (and which still works if I switch back to it) is: uwsgi --http :8000 --master --enable-threads --module Cats.wsgi

The command that works using runserver is: python manage.py runserver 0.0.0.0:8000

The commands that fail for the requests that work with the 2 other commands are: daphne -b 0.0.0.0 -p 8000 Cats.asgi:channel_layer python manage.py runworker

Other info: I added 'channels' in the installed apps (in settings.py)

other settings.py relevant info

CHANNEL_LAYERS = {
"default": {
    "BACKEND": "asgi_redis.RedisChannelLayer",
    "ROUTING": "Cats.routing.app_routing",
    "CONFIG": {
        "hosts": [(os.environ['REDIS_HOST'], 6379)],
    },
},

}

Cats/routing.py

from channels.routing import route, include
from main.routing import routing as main_routing

app_routing = [
    include(main_routing, path=r"^/ws/main"),
]

main/routing.py

from channels.routing import route, include

http_routing = [
]

stream_routing = [
    route('websocket.receive', 'main.consumers.ws_echo'), #just for test once it will work
]

routing = [
    include(stream_routing),
    include(http_routing),
]

main/consumers.py

def ws_echo(message):
message.reply_channel.send({
    'text': message.content['text'],
})

#this consumer is just for test once it will work

Any idea what could be wrong? All help much appreciated! Ty

EDIT: I tried a new thing:

python manage.py runserver 0.0.0.0:8000 --noworker
python manage.py runworker

And this does not work, while python manage.py runserver 0.0.0.0:8000 was working...

Any idea that could help?

Upvotes: 4

Views: 4826

Answers (2)

frmdstryr
frmdstryr

Reputation: 21362

If you're using heroku or dokku make sure you've properly set the "scale" to include the worker process. By default they will only run the web instance and not the worker!

For heroku

heroku ps:scale web=1:free worker=1:free

For dokku create a file named DOKKU_SCALE and add in:

web=1
worker=1

See:

Upvotes: 1

Itamar Lavender
Itamar Lavender

Reputation: 1028

channels will use default views for un-routed requests. assuming you use the javascripts right, I suggest you use only your default Cats/routing.py file as following:

from channels.routing import route
from main.consumers import *


app_routing = [
    route('websocket.connect', ws_echo, path="/ws/main")
]

or with reverse to help with your path

from django.urls import reverse

from channels.routing import route
from main.consumers import *


app_routing = [
    route('websocket.connect', ws_echo, path=reverse('main view name'))
]

I think also your consumer should be changed. when browser connects using websockets the server should first handle adding message reply channel. something like:

def ws_echo(message):
    Group("notifications").add(message.reply_channel)
    Group("notifications").send({
        "text": json.dumps({'testkey':'testvalue'})
    })

the send function should probably be called up on different event and the "notifications" Group should probably changed to have a channel dedicated to the user. something like

from channels.auth import channel_session_user_from_http

@channel_session_user_from_http
def ws_echo(message):
    Group("notify-private-%s" % message.user.id).add(message.reply_channel)
    Group("notify-private-%s" % message.user.id).send({
        "text": json.dumps({'testkey':'testvalue'})
    })

Upvotes: 2

Related Questions