I did a chatting part in my web app with django it works perfectly in local but after I deployed it to heroku , it doesn't work:
this is my consumers.py class:
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.models import User
from django.db.models import Q
from asgiref.sync import sync_to_async
import json
from chat.models import Thread, Message
from users.serializers import UserSerializer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
friend = None
me = self.scope['user'] # logged in user
friend_name = self.scope['url_route']['kwargs']['friend'] # get the username of that user, whoom you want to chat
friend_instance = await sync_to_async(User.objects.get, thread_sensitive=True)(username=friend_name) # get user object of friend
# create a new Thread object if thread of specific chat does not exists, otherwise return the thread
thread = None
try:
thread = await sync_to_async(Thread.objects.get, thread_sensitive=True)((Q(user1=me) & Q(user2=friend_instance)) | (Q(user1=friend_instance) & Q(user2=me)))
except:
thread = await sync_to_async(Thread.objects.create, thread_sensitive=True)(user1=me, user2=friend_instance)
self.room_name = thread.room_name # room name
await self.channel_layer.group_add(
self.room_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
'''
disconnect the websocket connection.
'''
await self.channel_layer.group_discard (
self.room_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
from_user = text_data_json['user']
to_user = text_data_json['friend']
from_user_instanse = await sync_to_async(User.objects.get, thread_sensitive=True)(username=from_user['username']) # get user object of friend
to_user_instanse = await sync_to_async(User.objects.get, thread_sensitive=True)(username=to_user['username']) # get user object of friend
thread_obj = await sync_to_async(Thread.objects.get, thread_sensitive=True)((Q(user1=from_user_instanse) & Q(user2=to_user_instanse)) | (Q(user1=to_user_instanse) & Q(user2=from_user_instanse)))
message_instane = await sync_to_async(Message.objects.create, thread_sensitive=True)(messag_body=message, from_user=from_user_instanse, to_user=to_user_instanse, thread=thread_obj)
await self.channel_layer.group_send(
self.room_name,
{
'type': 'chatroom_messages',
'message': message_instane.messag_body,
'user': message_instane.from_user
}
)
async def chatroom_messages(self, event):
message = event['message']
user = event['user']
user_serialized_data = UserSerializer(user)
await self.send(text_data=json.dumps({
'message': message,
'user': user_serialized_data.data
}))
in the template this is the connection request part with js:
chatSocket.send(JSON.stringify({ //the error shows in this part that WebSocket is already in CLOSING or CLOSED state
'message': msg,
'user': me,
'friend': friendName
}));
....
/* connection request */
const chatSocket = new WebSocket(
'wss://'
+ window.location.host
+ '/ws/chat/'
+ friendName['username']
+ '/'
);
in my chat app this I have routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<friend>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
in my project I have also routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter ({
'websocket': AuthMiddlewareStack(
URLRouter (
chat.routing.websocket_urlpatterns
)
)
})
and this is actually my asgi.py :
import os
import django #added
#from django.core.asgi import get_asgi_application #removed
from channels.routing import get_default_application # added
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
django.setup() #added
application = get_default_application() #added
#application = get_asgi_application() #removed
in my procfile :
release: python manage.py migrate
web: daphne myproject.asgi:application --port $PORT --bind 0.0.0.0 -v2
worker: python manage.py runworker channels --settings=myproject.settings -v2
in my settings.py
ASGI_APPLICATION = "myproject.routing.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')],
},
},
}
it works perfectly in local but not anymore working after deployement, am I messing something
this is heroku logs :
......
2021-12-29T18:15:42.187913+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packa
ges/aioredis/connection.py", line 111, in create_connection
2021-12-29T18:15:42.187913+00:00 app[web.1]: reader, writer = await asyncio.wait_for(open_conne
ction(
2021-12-29T18:15:42.187916+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/ta
sks.py", line 442, in wait_for
2021-12-29T18:15:42.187916+00:00 app[web.1]: return await fut
2021-12-29T18:15:42.187916+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packa
ges/aioredis/stream.py", line 23, in open_connection
2021-12-29T18:15:42.187916+00:00 app[web.1]: transport, _ = await get_event_loop().create_conne
ction(
2021-12-29T18:15:42.187917+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/ba
se_events.py", line 1056, in create_connection
2021-12-29T18:15:42.187917+00:00 app[web.1]: raise exceptions[0]
2021-12-29T18:15:42.187917+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/ba
se_events.py", line 1041, in create_connection
2021-12-29T18:15:42.187917+00:00 app[web.1]: sock = await self._connect_sock(
2021-12-29T18:15:42.187917+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/ba
se_events.py", line 955, in _connect_sock
2021-12-29T18:15:42.187918+00:00 app[web.1]: await self.sock_connect(sock, address)
2021-12-29T18:15:42.187918+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/se
lector_events.py", line 502, in sock_connect
2021-12-29T18:15:42.187918+00:00 app[web.1]: return await fut
2021-12-29T18:15:42.187918+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/se
lector_events.py", line 537, in _sock_connect_cb
2021-12-29T18:15:42.187919+00:00 app[web.1]: raise OSError(err, f'Connect call failed {address}
')
2021-12-29T18:15:42.187919+00:00 app[web.1]: ConnectionRefusedError: [Errno 111] Connect call f
ailed ('127.0.0.1', 6379)
2021-12-29T18:15:42.188173+00:00 app[web.1]: 2021-12-29 18:15:42,188 INFO failing WebSocket
opening handshake ('Internal server error')
2021-12-29T18:15:42.188439+00:00 app[web.1]: 2021-12-29 18:15:42,188 WARNING dropping connecti
on to peer tcp4:10.1.93.62:26296 with abort=False: Internal server error
2021-12-29T18:15:42.189132+00:00 app[web.1]: 2021-12-29 18:15:42,189 DEBUG WebSocket closed
for ['10.1.93.62', 26296]
2021-12-29T18:15:42.189195+00:00 app[web.1]: 10.1.93.62:26296 - - [29/Dec/2021:18:15:42] "WSDIS
CONNECT /ws/chat/person/" - -
Related
I am coding telegram-bot, using Flask and asyncio. And I have a function for receiving and processing requests - get_messages(). And test function - sleep_func(), which will sleep for a while(20 seconds), but get_messages() will be working at this moment, when sleep_func() is sleeping. After 20 seconds sleep_func() must wake up, print some text, but doesn't finish the get_messages(). So, I want to implement asynchrony. I tried to do this, using asyncio.create_task, that worked, but only for one request(see at the comments in get_messages()). Now I tried another way, but it gives me this error:
ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='Task-7' coro=<get_messages() done, defined at D:\Python_Labs\FilmMarketBot\fm_bot.py:31> exception=RuntimeError('Timeout context manager should be used inside a task')>
Traceback (most recent call last):
File "D:\Python_Labs\FilmMarketBot\fm_bot.py", line 37, in get_messages
await message(bot)
File "D:\Python_Labs\FilmMarketBot\bot_files\message_handling.py", line 14, in message
await commands(bot, chat_id)
File "D:\Python_Labs\FilmMarketBot\bot_files\messages\bot_commands\commands.py", line 11, in commands
await start(bot, chat_id)
File "D:\Python_Labs\FilmMarketBot\bot_files\messages\bot_commands\commands_methods.py", line 11, in start
await bot.send_message(chat_id, f"Hello, {first_name}!")
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiogram\bot\bot.py", line 339, in send_message
result = await self.request(api.Methods.SEND_MESSAGE, payload)
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiogram\bot\base.py", line 231, in request
return await api.make_request(await self.get_session(), self.server, self.__token, method, data, files,
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiogram\bot\api.py", line 139, in make_request
async with session.post(url, data=req, **kwargs) as response:
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiohttp\client.py", line 1138, in __aenter__
self._resp = await self._coro
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiohttp\client.py", line 466, in _request
with timer:
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiohttp\helpers.py", line 701, in __enter__
raise RuntimeError(
RuntimeError: Timeout context manager should be used inside a task
This is my fm_bot.py:
# бібліотека request для отримання запитів(повідомлень), які надходять на Flask-сервер від Телеграм-серверу і Stripe
from flask import Flask, request
import logging
from aiogram import Bot, Dispatcher
from threading import Thread
# from dotenv import load_dotenv
from time import sleep
import asyncio
import time
from bot_files.models import db, Users, Films, Purchases
from bot_files.message_handling import message, callback_query, object, other_messages
# from bot_files.other import technical_works
from bot_files.chat_member import delete_user
from bot_files.stripe import create_stripe_webhook
from bot_files.objects.stripe_requests_files.purchases import add_purchase
from bot_files.config import *
logging.basicConfig(level=logging.INFO)
bot = Bot(token=bot_token)
dp = Dispatcher(bot)
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = db_url
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
db.init_app(app)
async def get_messages():
print("Flag")
print(request.json)
message_type = list(request.json.keys())[1]
if message_type == 'message':
await message(bot)
elif message_type == 'callback_query':
await callback_query(bot, payment_token, stripe_token)
elif message_type == 'my_chat_member':
chat_id = request.json["my_chat_member"]["chat"]["id"]
if request.json["my_chat_member"]["new_chat_member"]["status"] == "kicked":
await delete_user(chat_id)
elif message_type == 'object':
await object(bot)
elif message_type == 'pre_checkout_query':
# Номер карти - 4242 4242 4242 4242
chat_id = request.json["pre_checkout_query"]["from"]["id"]
await add_purchase(bot, chat_id)
pre_checkout_query_id = request.json["pre_checkout_query"]["id"]
await bot.answer_pre_checkout_query(pre_checkout_query_id, True)
# else:
# await other_messages(bot)
return {"get_messages is ok": True}
async def sleep_func():
await asyncio.sleep(20)
print("Hello!")
#app.route('/' + bot_token, methods=['POST'])
async def asynchrony():
#start_time = time.time()
#get_messages_task = asyncio.create_task(get_messages())
#sleep_task1 = asyncio.create_task(sleep_func())
#sleep_task2 = asyncio.create_task(sleep_func())
#await asyncio.wait([get_messages_task, sleep_task1])
#print(time.time() - start_time)
get_messages_task = asyncio.create_task(get_messages())
await asyncio.wait([get_messages_task])
#await get_messages()
return {"asynchrony is ok": True}
#app.route('/')
async def webhook():
await bot.delete_webhook()
await bot.set_webhook(url=app_url)
# chat_id = None
await create_stripe_webhook(app_url, payment_token)
# await test_users()
# await test_films()
# await test_purchases()
# await test_discounts()
# while True:
# time_now = datetime.datetime.now()
# if datetime.date.today().isoweekday() == 4:
# await technical_works(bot)
sleep_task = asyncio.create_task(sleep_func())
await asyncio.wait([sleep_task])
return '!', 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
I will be very appreciative for you help!
My answer is not suitable for this code, which is Flask-app. But it is answer for this question. So you must use Aiogram for asynchrony receiving and processing messages. Aiogram has special decorator async_task for this: https://docs.aiogram.dev/en/latest/_modules/aiogram/dispatcher/dispatcher.html#Dispatcher.async_task.
If your bot at the same time must perform another function, for example, change data in database once a day, you can create button only for administrator that will start infinity cycle which will sleep for number of seconds in one day.
I did a chatting part in my web app with django it works perfectly in local but after I deployed it to heroku , it doesn't work:
this is my consumers.py class:
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.models import User
from django.db.models import Q
from asgiref.sync import sync_to_async
import json
from chat.models import Thread, Message
from users.serializers import UserSerializer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
friend = None
me = self.scope['user'] # logged in user
friend_name = self.scope['url_route']['kwargs']['friend'] # get the username of that user, whoom you want to chat
friend_instance = await sync_to_async(User.objects.get, thread_sensitive=True)(username=friend_name) # get user object of friend
# create a new Thread object if thread of specific chat does not exists, otherwise return the thread
thread = None
try:
thread = await sync_to_async(Thread.objects.get, thread_sensitive=True)((Q(user1=me) & Q(user2=friend_instance)) | (Q(user1=friend_instance) & Q(user2=me)))
except:
thread = await sync_to_async(Thread.objects.create, thread_sensitive=True)(user1=me, user2=friend_instance)
self.room_name = thread.room_name # room name
await self.channel_layer.group_add(
self.room_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
'''
disconnect the websocket connection.
'''
await self.channel_layer.group_discard (
self.room_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
from_user = text_data_json['user']
to_user = text_data_json['friend']
from_user_instanse = await sync_to_async(User.objects.get, thread_sensitive=True)(username=from_user['username']) # get user object of friend
to_user_instanse = await sync_to_async(User.objects.get, thread_sensitive=True)(username=to_user['username']) # get user object of friend
thread_obj = await sync_to_async(Thread.objects.get, thread_sensitive=True)((Q(user1=from_user_instanse) & Q(user2=to_user_instanse)) | (Q(user1=to_user_instanse) & Q(user2=from_user_instanse)))
message_instane = await sync_to_async(Message.objects.create, thread_sensitive=True)(messag_body=message, from_user=from_user_instanse, to_user=to_user_instanse, thread=thread_obj)
await self.channel_layer.group_send(
self.room_name,
{
'type': 'chatroom_messages',
'message': message_instane.messag_body,
'user': message_instane.from_user
}
)
async def chatroom_messages(self, event):
message = event['message']
user = event['user']
user_serialized_data = UserSerializer(user)
await self.send(text_data=json.dumps({
'message': message,
'user': user_serialized_data.data
}))
in the template this is the connection request part with js:
chatSocket.send(JSON.stringify({ //the error shows in this part that WebSocket is already in CLOSING or CLOSED state
'message': msg,
'user': me,
'friend': friendName
}));
....
/* connection request */
const chatSocket = new WebSocket(
'wss://'
+ window.location.host
+ '/ws/chat/'
+ friendName['username']
+ '/'
);
in my chat app this I have routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<friend>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
in my project I have also routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter ({
'websocket': AuthMiddlewareStack(
URLRouter (
chat.routing.websocket_urlpatterns
)
)
})
and this is actually my asgi.py :
import os
import django #added
#from django.core.asgi import get_asgi_application #removed
from channels.routing import get_default_application # added
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
django.setup() #added
application = get_default_application() #added
#application = get_asgi_application() #removed
in my procfile :
release: python manage.py migrate
web: daphne myproject.asgi:application --port $PORT --bind 0.0.0.0 -v2
worker: python manage.py runworker channels --settings=myproject.settings -v2
in my settings.py
ASGI_APPLICATION = "myproject.routing.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')],
},
},
}
it works perfectly in local but not anymore working after deployement, am I messing something
I was following this tutorial on testdriven.io in order to test some async functions in django and I need to add som decorators in order to enable my async tests to access to the DB.
However, I get the following error message:
Error
Traceback (most recent call last):
File "/Users/apple/.local/share/virtualenvs/backend-KucV-wUh/lib/python3.9/site-packages/django/db/backends/base/base.py", line 237, in _cursor
return self._prepare_cursor(self.create_cursor(name))
File "/Users/apple/.local/share/virtualenvs/backend-KucV-wUh/lib/python3.9/site-packages/django/db/backends/sqlite3/base.py", line 274, in create_cursor
return self.connection.cursor(factory=SQLiteCursorWrapper)
sqlite3.ProgrammingError: Cannot operate on a closed database.
#database_sync_to_async
def create_user():
user = User.objects.create(
username='username',
)
user.set_password('password')
user.save()
#pytest.mark.asyncio
#pytest.mark.django_db(transaction=True)
class WebsocketTests(TestCase):
async def test_not_authenticated(self):
await create_user()
..... other async functions
make sure to `pip install pytest pytest-django pytest-asyncio
use #pytest.mark.asyncio and #pytest.mark.django_db(transaction=True)
from asgiref.sync import sync_to_async
from channels.db import database_sync_to_async
#database_sync_to_async
def MakeMessage():
Message.objects.create()
#pytest.mark.asyncio
#pytest.mark.django_db(transaction=True)
class TestWebSocket:
async def test_connection(self, settings):
communicator = WebsocketCommunicator( application=application, path=path )
connected, _ = await communicator.connect()
assert connected
m = await sync_to_async(Message.objects.count)()
assert m == 0
await MakeMessage()
m = await sync_to_async(Message.objects.count)()
assert m == 1
#settings.py
WSGI_APPLICATION = 'frosty_breeze_25125.wsgi.application'
ASGI_APPLICATION = 'frosty_breeze_25125.asgi.application'
host = os.environ.get('REDIS_URL', 'redis://localhost:6379') if IS_DEPLOYED else ('0.0.0.0', 6379)
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [host],
},
},
}
REDIS_URL = [host]
I've checked all the related questions none of them helped me. I'm making a simple application where I will get JSON responses on the websocketking.com site. No matter how many times I make changes none of the tries are working.
here every detail
asgi.py file
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import path
from home.consumers import *
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
ws_patterns = [
path('ws/test/', TestConsumer),
]
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(URLRouter(ws_patterns)),
})
consumers.py file
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
import json
class TestConsumer(WebsocketConsumer):
def connect(self):
self.room_name = "test_consumer"
self.room_group_name = "test_consumer_group"
async_to_sync(self.channel_layer.group_add)(self.room_name, self.room_group_name)
self.accept()
self.send(text_data=json.dumps({'status' : 'connected'}))
def receive(self):
pass
def disconnect(self):
pass
These are the things I added in my settings.py file
ASGI_APPLICATION = 'core.asgi.application'
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
},
},
}
And here is the error I got in console
WebSocket HANDSHAKING /ws/test/ [127.0.0.1:2795]
Exception inside application: object.__init__() takes exactly one argument (the instance to initialize)
Traceback (most recent call last):
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\routing.py", line 71, in __call__
return await application(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\sessions.py", line 254, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\auth.py", line 181, in __call__
return await super().__call__(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\routing.py", line 150, in __call__
return await application(
File "D:\PythonProjects\channels_project\env\lib\site-packages\asgiref\compatibility.py", line 33, in new_application
instance = application(scope)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\generic\websocket.py", line 23, in __init__
super().__init__(*args, **kwargs)
TypeError: object.__init__() takes exactly one argument (the instance to initialize)
WebSocket DISCONNECT /ws/test/ [127.0.0.1:2795]
Maybe you've already solved this issue but here's some things I've noticed with your code which might help you:
Change WebsocketConsumer to AsyncWebsocketConsumer- Then change your consumer methods to async, e.g.
async def connect(self):
...
Change
async_to_sync(self.channel_layer.group_add)(self.room_name,
self.room_group_name)
to
await self.channel_layer.group_add(
self.room_name,
self.room_group_name)
As a side note:
You don't need to do self.send(msg) to check if you are connected. This is handled in self.accept() and will return to
client.onopen = () => {
console.log("connected");
}
If none of these sort your problem it may be an issue with your routing and/or the middleware stack. I have had issues with authentication recently. Try using a different browser if you're using Chrome and see if you can tag onto the onerror event
I am attempting to use Django Channels on my digital ocean ubuntu 18 droplet to serve a 'video' feed from a webcam attached to a raspberry pi. The raspberry pi is using python websockets-client and openCV to send jpeg images quickly (every 0.05 seconds).
The issue is that this will work great, streaming for 10+ minutes perfectly with good frame rate, and I will think I have got it working. Then the next day, it will stream for about 10 seconds before the frame rate on the browser becomes very laggy and then stops receiving images. The Raspberry pi then disconnects from the websocket from what appears to be my server disconnecting it, I cannot get it to stream longer than a few seconds. Why would it work perfectly one day, and not the next? Nothing obvious has changed.
The reason I think it is my server/channels/redis causing the problem:
Before the raspberry pi even seems to be having any trouble, the video feed being shown on the browser will become very laggy then will stop getting new images entirely. Then a few seconds later my raspberry pi socket will disconnect from the websocket.
What I have Tried:
I have done a speed test on my internet connection and it is 50mb down/15mb up.
I have tried reducing the frame rate to something very slow, like one image per second, and it will still stop working.
I have checked on my memory and CPU of both my droplet and raspberry pi. When a crash occurs, CPU on both machines are typically below 25% usage, and memory is typically below 40% usage.
I changed some of the redis settings in settings.py shown below, did not seem to have any effect.
What would cause an intermittently functioning websocket? I have changed my redis settings in settings.py, but I am not sure what else to change or attempt to debug.
I followed the tutorial on deployment here: Channels Docs
Channels code:
Consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
#import urllib.parse as urlparse
#from urllib.parse import parse_qs
from channels.db import database_sync_to_async
from accounts.models import CustomUser
from rest_framework.authtoken.models import Token
#websocket for video connection
class StreamConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['device_id']
print(self.room_name)
self.room_group_name = 'stream_%s' % self.room_name
self.token_passed = self.scope['query_string'].decode() #token passed in with ws://url/?token
self.token_actual = await self.get_token(self.room_name) #go get actual token for this monitor
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
#check to ensure tokens match, if they do, allow connection
if self.token_passed == self.token_actual:
await self.accept()
print('True')
else:
print(self.token_passed)
print(self.token_actual)
await self.close()
async def disconnect(self, close_code):
# Leave room group
print(close_code)
print("closing")
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
#database_sync_to_async
def get_token(self, monitor_serial):
try:
monitor = CustomUser.objects.get(deviceSerial=monitor_serial)
token = Token.objects.get(user=monitor)
return token.key
except:
return "user not available"
# Receive message from WebSocket
async def receive(self, bytes_data=None, text_data=None):
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'stream_data',
'bytes_data': bytes_data,
'text_data': text_data
}
)
# Receive message from room group
async def stream_data(self, event):
bytes_data = event['bytes_data']
text_data = event['text_data']
# Send message to WebSocket
await self.send(bytes_data=bytes_data, text_data=text_data)
class StatusConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['device_id']
self.room_group_name = 'status_%s' % self.room_name
self.token_passed = self.scope['query_string'].decode() #token passed in with ws://url/?token
self.token_actual = await self.get_token(self.room_name) #go get actual token for this monitor
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
#check to ensure tokens match, if they do, allow connection
if self.token_passed == self.token_actual:
await self.accept()
else:
print(self.token_passed)
print(self.token_actual)
await self.close()
async def disconnect(self, close_code):
# Leave room group
print(close_code)
print("closing")
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
#database_sync_to_async
def get_token(self, monitor_serial):
monitor = CustomUser.objects.get(deviceSerial=monitor_serial)
token = Token.objects.get(user=monitor)
return token.key
# Receive message from WebSocket
async def receive(self, text_data=None):
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'status_data',
'text_data': text_data
}
)
# Receive message from room group
async def status_data(self, event):
#bytes_data = event['bytes_data']
text_data = event['text_data']
# Send message to WebSocket
#await self.send(bytes_data=bytes_data, text_data=text_data)
await self.send(text_data=text_data)
asgi.py
import os
from django.conf.urls import url
from django.core.asgi import get_asgi_application
# Fetch Django ASGI application early to ensure AppRegistry is populated
# before importing consumers and AuthMiddlewareStack that may import ORM
# models.
try:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "polysensesite.settings")
except:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
django_asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from .consumers import StreamConsumer, StatusConsumer
application = ProtocolTypeRouter({
# Django's ASGI application to handle traditional HTTP requests
"http": django_asgi_app,
# WebSocket chat handler
"websocket": AuthMiddlewareStack(
URLRouter([
url(r"^stream/(?P<device_id>[\d\-]+)/$", StreamConsumer.as_asgi()),
url(r"^status/(?P<device_id>[\d\-]+)/$", StatusConsumer.as_asgi()),
])
),
})
settings.py
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
"capacity": 5000,
"expiry": 5,
},
},
}
I put some print statements on the browser, consumers, and raspberry pi, and it seems like everything is working except the raspberry pi stops receiving messages (but is still attempting to send images) then the socket closes abruptly and I get this message from Daphne:
127.0.0.1:37154 - - [30/May/2021:16:56:32] "WSDISCONNECT /stream/XXXXXXX/" - -
1001
closing
2021-05-30 16:56:33,364 ERROR Exception inside application:
Traceback (most recent call last):
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/routing.py", line 71, in __call__
return await application(scope, receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/sessions.py", line 254, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/auth.py", line 181, in __call__
return await super().__call__(scope, receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/routing.py", line 160, in __call__
send,
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 94, in app
return await consumer(scope, receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 59, in __call__
[receive, self.channel_receive], self.dispatch
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/utils.py", line 51, in await_many_dispatch
await dispatch(result)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 73, in dispatch
await handler(message)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/generic/websocket.py", line 198, in websocket_receive
await self.receive(bytes_data=message["bytes"])
File "./polysensesite/consumers.py", line 69, in receive
'text_data': text_data
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels_redis/core.py", line 667, in group_send
key, min=0, max=int(time.time()) - int(self.expiry)
aioredis.errors.ConnectionForcedCloseError