Django Channels enables a developer to use WebSockets and other non-HTTP protocols in any Django project. By, using non-HTTP protocol the website can receive real-time updates from the server. The most common use case for Django Channels would be when we want to implement a chat application. Chat applications need to be real-time and seamless and that is where Django channels offer great benefits over using traditional Ajax-based request-response chat models. Unfortunately, new developers might find Django channels both hard to implement and difficult to deploy. Luckily, its official documentation covers a great tutorial on implementation which I will be linking below. Django channels require ASGI (Asynchronous Server Gateway Interface) for deployment. We will be using Daphne for ASGI. WSGI (Web Server Gateway Interface) will be used for the rest of the project deployment. For WSGI, Gunicorn will be used. The tutorial is of an intermediate level and some prerequisite tutorial needs to be followed beforehand.
Prerequisite Tutorials
- Simple Add Object functionality in Django for showcasing usage of WSGI
- Setting up the server for Django WSGI Deployment with Nginx, Gunicorn (Section A only. Modify it to host prerequisite 1)
- Official Django Channel Tutorial for showcasing usage of ASGI (Part 1 & 2 only)
Deployment of both WSGI and ASGI using Nginx and Supervisor
This tutorial mostly follows the official documentation with some modifications.
Replace the projects asgi.py file content with this:
import os
from django.conf.urls import url
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_examples.settings')
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
django_asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
import chat.routing
application = ProtocolTypeRouter({
"http": django_asgi_app,
'websocket': AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
)
})
Since, Nginx should be already installed in first prerequisite we only need to install Supervisor.
$ sudo apt install supervisor
Let us now make a config file called channels.conf inside /etc/supervisor/conf.d/ -The supervisor will listen on a TCP port and then pass the socket to the child process for further processing. The file will contain this :
[fcgi-program:asgi]
# TCP socket used by Nginx backend upstream
socket=tcp://localhost:8000
# Directory where your site's project files are located
directory=/home/saif/django_examples
# Each process needs to have a separate socket file, so we use process_num
# Make sure to update "django_examples.asgi" to match your project name
command=/home/saif/django_examples/djangovirtualenv/bin/daphne -u /run/daphne/daphne%(process_num)d.sock --fd 0 --access-log - --proxy-headers django_examples.asgi:application
# Number of processes to startup, roughly the number of CPUs you have
numprocs=4
# Give each process a unique name so they can be told apart
process_name=asgi%(process_num)d
# Automatically start and recover processes
autostart=true
autorestart=true
# Choose where you want your log to go
stdout_logfile=/var/log/asgi.log
redirect_stderr=true
Create the run directory for the sockets referenced in the supervisor configuration file.
$ sudo mkdir /run/daphne/
If the supervisor fcgi-program is being run under a different user make sure to change the owner settings of the run directory.
$ sudo chown <user>.<group> /run/daphne/
On every server reboot, the /run/daphne folder will be cleared out. To make it persistent create a file /usr/lib/tmpfiles.d/daphne.conf and add the following in its content:
$ d /run/daphne 0755 <user> <group>
Make sure the supervisor reread and update itself with the new job:
$ sudo supervisorctl reread
$ sudo supervisorctl update
Next, put the following setting in your Nginx Config file:
upstream ws_server {
server localhost:8000;
}
server {
listen 80;
server_name 'mywebsite.com';
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/saif/django_examples;
}
# WSGI
location / {
try_files $uri @proxy_to_app;
}
# ASGI
location /ws {
try_files $uri @proxy_to_ws;
}
location @proxy_to_ws {
proxy_pass http://ws_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location @proxy_to_app {
include proxy_params;
proxy_pass http://unix:/home/saif/django_examples/django_examples.sock;
}
}
Reload Nginx to apply the new changes:
$ sudo service nginx reload
That’s it. Your server should be able to serve both ASGI and WSGI based apps now. In case you faced some issue. I have followed the official Django Channels tutorial and updated the code in my Github repository. The tutorial is followed in the chat app. Simply download and deploy it in your local machine.
Md. Saifur Rahman is a Full Stack Django and Laravel Developer. Additionally, he loves to spend time learning and researching about the Internet of Things (IoT). He loves to share his work and contribute to helping fellow developers.