Я использую комбинацию DRF 3.11.0 и каналов 2.4.0 для реализации бэкэнда, и он размещен на Heroku 1 dyno с подключенным ресурсом Redis. У меня есть сокет на моем интерфейсе React, который успешно отправляет / получает от внутреннего сервера.
У меня возникают проблемы, когда любое сообщение, отправляемое обратно во внешний интерфейс через сокет, отправляется дважды. Через console.log
я подтвердил, что передний конец пингует задний конец только один раз. Я могу подтвердить через print()
внутри вызова API, что функция также вызывает async_to_sync(channel_layer.group_send)
только один раз. Проблема исходит от моего потребителя - когда я использую print(self.channel_name)
внутри share_document_via_videocall()
, я вижу, что вызываются два экземпляра с разными self.channel_name
(specific.AOQenhTn!fUybdYEsViaP
и specific.AOQenhTn!NgtWxuiHtHBw
. Похоже, что потребитель имеет подключен к двум отдельным каналам, но я не уверен, почему. Когда я помещаю операторы print()
в свой connect()
, я вижу его только go через процесс подключения один раз.
Как я могу гарантировать, что Я подключен только к одному каналу?
в settings.py
:
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
#"hosts": [('127.0.0.1', 6379)],
"hosts": [(REDIS_HOST)],
},
},
}
Потребитель:
import json
from asgiref.sync import async_to_sync
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncWebsocketConsumer
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from .exceptions import ClientError
import datetime
from django.utils import timezone
class HeaderConsumer(AsyncWebsocketConsumer):
async def connect(self):
print("connecting")
await self.accept()
print("starting")
print(self.channel_name)
await self.send("request_for_token")
async def continue_connect(self):
print("continuing")
print(self.channel_name)
await self.get_user_from_token(self.scope['token'])
await self.channel_layer.group_add(
"u_%d" % self.user['id'],
self.channel_name,
)
#... more stuff
async def disconnect(self, code):
await self.channel_layer.group_discard(
"u_%d" % self.user['id'],
self.channel_name,
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
if 'token' in text_data_json:
self.scope['token'] = text_data_json['token']
await self.continue_connect()
async def share_document_via_videocall(self, event):
# Send a message down to the client
print("share_document received")
print(event)
print(self.channel_name)
print(self.user['id'])
await self.send(text_data=json.dumps(
{
"type": event['type'],
"message": event["message"],
},
))
@database_sync_to_async
def get_user_from_token(self, t):
try:
print("trying token" + t)
token = Token.objects.get(key=t)
self.user = token.user.get_profile.json()
except Token.DoesNotExist:
print("failed")
self.user = AnonymousUser()
Вызов API REST:
class ShareViaVideoChat(APIView):
permission_classes = (permissions.IsAuthenticated,)
def post(self, request, format=None):
data = request.data
recipient_list = data['recipient_list']
channel_layer = get_channel_layer()
for u in recipient_list:
if u['id'] != None:
print("sending to:")
print('u_%d' % u['id'])
async_to_sync(channel_layer.group_send)(
'u_%d' % u['id'],
{'type': 'share_document_via_videocall',
'message': {
'document': {'data': {}},
'sender': {'name': 'some name'}
}
}
)
return Response()