so i was trying to do some basic stuff with channels.
i wrote a script in my html to return a message
script
<script>
const chatsocket = new WebSocket(
'ws://'+window.location.host+'/ws/test/'
)
chatsocket.onmessage = function (e) {
const data = JSON.parse(e.data)
console.log(data)
chatsocket.close()
}
</script>
my consumers.py
from channels.exceptions import StopConsumer
from channels.generic.websocket import AsyncWebsocketConsumer
import json
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.group_name = 'notificationgroup'
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
await self.channel_layer.group_send(
self.group_name,
{
'type': 'tester_message',
'tester': 'hello world'
}
)
async def tester_message(self, event):
tester = event['tester']
await self.send(text_data=json.dumps({
'tester': tester
}))
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.group_name,
self.channel_name
)
raise StopConsumer()
so basically i get my desired output in the console... when i have
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer'
}
}
but when i use an aws redis cluster (after installing channels-redis obviously)
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('aws redis endpoint', 6379)],
},
},
}
i get this error
WebSocket HANDSHAKING /ws/test/ [127.0.0.1:51551]
WebSocket DISCONNECT /ws/test/ [127.0.0.1:51551]
Application instance <Task pending name='Task-4' coro=<StaticFilesWrapper.__call__() running at E:\intes\sync\shero\venv\lib\site-packages\channels\staticfiles.py:44> wait_for=<Future pending cb=[BaseSelectorEventLoop._sock_write_done(1216)(), <TaskWakeupMethWrapper object at 0x000001F7D5226C70>()]>> for connection <WebSocketProtocol client=['127.0.0.1', 51551] path=b'/ws/test/'> took too long to shut down and was killed.
what am I doing wrong?
I was doing it all wrong. Apparently, you cannot get the aws-redis working outside. you must have your application running on an EC2 within the same VPN. Then, it works!
Related
I'm trying to integrate celery with django channels but channels consumer is not working as it supposed. Logging or prints inside consumer functions are not displaying message in terminal.
The following celery task is called from signals.py
tasks.py
from channels.layers import get_channel_layer
#app.task
def send_request_notification():
text = 'You have new cleaning request'
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'companies_received_request', # group name
{
'type': 'send_notification',
'text': text
}
)
print("SENT ---") # can see result of this print
consumers.py
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
logger.info("Connected to websocket")
await self.channel_layer.group_add(
'companies_received_request', self.channel_name
)
await self.accept()
async def disconnect(self):
await self.channel_layer.group_discard(
'companies_received_request', self.channel_name
)
logger.info("Disconnected from websocket")
async def send_notification(self, event):
logger.info("Here in sending notification")
text_message = event['text']
await self.send(text_message)
print("EVENT.TEXT")
print(text_message)
settings.py
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [('127.0.0.1', 6379)],
},
},
}
I am supposed to get the output of prints and logging of consumer functions in terminal but I'm getting nothing when I call channel_layer.group_send method from celery task. (It seems that celery couldn't make a connection to consumer)
I am trying to build a notifications system using Django Channels. I completed the intial setup and when I run my server I get confirmation of Handshake and connection confirmed. However, when I view my console log, I cannot see the message to be sent. I also put a print statement on the send_notification function but it is never reached. I am new to using Django Channels so any help would be appreciated. What am I terribly doing wrong?
Here is my ASGI file setup:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'clicksource.settings')
from channels.auth import AuthMiddleware, AuthMiddlewareStack
from notifications.routing import websocket_urlpatterns
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
)
})
Here is my consumers.py:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'notification_%s' % self.room_name
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from room group
async def send_notification(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message':message
}))
Here is my routing.py:
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/notification/(?P<room_name>\w+)/$', consumers.NotificationConsumer.as_asgi()),
]
And the script in my base.html file:
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const notificationSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/notification/'
+ roomName
+ '/'
);
notificationSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
//document.querySelector('#chat-log').value += (data.message + '\n');
console.log(data);
document.getElementById("notifications-dropdown").innerHTML = "<li class='dropdown-item'>" + data + "</li><hr class='dropdown-divider'>" + document.getElementById("notifications-dropdown").innerHTML;
document.getElementById("notification-badge").innerHTML = parseInt(document.getElementById("notification-badge").innerHTML) + 1;
};
notificationSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
</script>
You need to define .receive() method on NotificationConsumer. Whenever a client sends a message, .receive() gets it and echoes it back to the group and then everyone subscribed to that group gets it.
Check out this link: https://channels.readthedocs.io/en/stable/tutorial/part_3.html
I have a Django project and I'm trying to implement the Redis channel. When I add the config below, my app works.
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}
However, when I try to add the following config, I'm getting aioredis.errors.ProtocolError: Protocol error, got "H" as reply type byte error.
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 6379)],
},
},
}
Here is my consumers.py file;
class VideoCallSignalConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'Test-Room'
# print(self.scope["user"])
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
Btw, I'm using macOS. I already have installed Redis with brew install redis command and started the redis-server with daphne -p 6379 VideoConference.asgi:application command.
I am following this tutorial Channels Tutorial Link
My Goal was to make a simple asgi chat server. But It is showing weird behaviour. The message sent from one tab..should print "HI" in current tab..and also "HI" in the tab connected in the same room. but its printing the both "HI" in the current tab, no message is shown in the other tab connected in the same room.
my consumers.py is simeple, just from the tutorials file...
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept()
def disconnect(self, close_code):
# Leave room group
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
def chat_message(self, event):
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))
my settings file is configured to receive redis connection in 127.0.0.1. I am using docker redis image just s the tutorial said.
ASGI_APPLICATION = 'mysite.asgi.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
my asgi.py file...configured like the tutorial said-->
# mysite/asgi.py
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import chat.routing
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
),
})
This is the issue of the latest release.
I solved it by reinstalling the package: channels == 2.4.0
Then I changed the file asgi.py, commented out the line there: "http": get_asgi_application(),
also I removed as_asgi() from file routing.py
Here is the tutorial of the previous version.
I created a simple chat app using WebSockets after following the official Django-channels tutorial. However, I can't get it to work in production. I have done a few google searching to find out a forum in Pythonanywhere saying that they don't support WebSocket, I contacted the team and they told me the same thing.
I have done even more google searching and found things related to Daphne server, Nginx, and a few other things I never heard about before.
As I'm new to Django-channels I'm currently very confused! Is there something I can do to make my WebSocket website run normally in Pythonanywhere on production (for free of course) Or I have to delete all of the WebSocket code and replace it with repetitive Http called to check of new messages (With AJAX)?
And if there is no other solution but to move to repetitive Http calls, is there any other web hosting service that offers free play which includes free SSL certification, the domain name (such as mydomain.servicename.com) instead of random characters, and WebSocket support?
Thanks
the code i use
I don't know if it was relevant, also it's working perfect in development so i don't think there is an error in it
settings.py:
INSTALLED_APPS = [
'channels',
...
'django_cleanup',
]
ASGI_APPLICATION = 'orgachat.routing.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
main routing.py (in route folder)
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.routing import websocket_urlpatterns as chat_routing
application = ProtocolTypeRouter({
"websocket": AuthMiddlewareStack(
URLRouter(
chat_routing,
)
)
})
routing.py for chat app
from django.urls import path
from . import consumers
websocket_urlpatterns = [
path('ws/chat/room/<int:room_id>/', consumers.RoomConsumer),
]
consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class RoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.group_name = str(self.scope['url_route']['kwargs']['room_id'])
await self.channel_layer.group_add(self.group_name, self.channel_name)
await self.accept()
async def disconnect(self, code):
await self.channel_layer.group_discard(self.group_name, self.channel_layer)
async def receive(self, text_data):
message_json = json.loads(text_data)
await self.channel_layer.group_send(self.group_name, {
'type': 'send_message',
'content': message_json['content'],
'area': message_json['area'],
'area_id': message_json['area_id'],
'username': self.scope['user'].username,
})
async def send_message(self, event):
await self.send(json.dumps(event))
full js script
<script>
// -------------------
// WEBSOCKET SETUP
// -------------------
var wsStart = 'ws://'
var hostName = window.location.hostname + ':8000'
if (window.location.protocol.includes('https')) {
wsStart = 'wss://'
hostName = window.location.hostname
};
let endpoint = wsStart + hostName + '/ws' + window.location.pathname
console.log(endpoint)
var socket = new WebSocket(endpoint);
socket.onmessage = function (e) {
// todo not show message if in a different room
data = JSON.parse(e.data);
console.log(data.area_id)
console.log(data.area)
var sender = 'other'
var username = data.username
if (data.username == "{{ user.username }}") {
sender = 'self';
username = 'You'
}
document.querySelector('.messages').innerHTML += `
<div class="message ${sender}">
<p>${username} — ${data.area}:</p>
<p>${data.content}</p>
</div>
`
document.querySelector('#notification_sound').play()
}
socket.onerror = function (e) {
alert("SERVER ERROR 500, You won't be able to see messages unless you refresh,")
}
socket.onclose = function (e) {}
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('#sendMessage').onclick = function (e) {
e.preventDefault();
// ------------AJAX: SEND AND MESSAGE---------------
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function (e) {
if (this.readyState == 4 && this.status == 200) {
document.querySelector('#id_content').value = '';
}
}
xhr.open("POST", "{% url 'chat:room' room.id %}", true);
xhr.setRequestHeader('Content-type', "application/x-www-form-urlencoded");
data = {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'content': document.querySelector('#id_content').value,
'area': parseInt(document.querySelector('#id_area').value),
}
xhr.send(JSON.stringify(data));
// ---------------WEBSOCKET: ECHO MESSAGE---------------
let area = document.getElementById('id_area')
socket.send(JSON.stringify({
'content': document.querySelector('#id_content').value,
'area': area.options[area.selectedIndex].text,
'area_id': document.querySelector('#id_area').value
}));
}
});
</script>
Websockets, and therefore, django-channels are not supported on PythonAnywhere.