我正在使用 django-channels 和 redis 在 django 和 Angular 中实现聊天应用程序。
套接字已连接并正常工作,但我面临的问题是,当两个用户在线并使用相同的线程 url 连接同一个聊天室时,它会连接,但任何用户发送的消息仅发送给最近连接套接字的用户并且它们仅发送给该用户两次。
在 django 中我做了以下配置:
设置/base.py
....
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.sites',
'channels',
'chats'
]
ASGI_APPLICATION = "influnite.routing.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
# "hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')]
},
},
}
....
路由.py
from django.conf.urls import url
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator, OriginValidator
from chats.consumers import ChatConsumer
application = ProtocolTypeRouter({
'websocket': AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter(
[
url(r"^messages/(?P<thread_id>[\w.+]+)/", ChatConsumer())
]
)
)
)
})
我创建了三个模型,即Thread
, ThreadMember
& ChatMessage
.
聊天/models.py
from django.db import models
from django.db.models import Q
from django.utils import timezone
from django.contrib.auth.models import User
from base.models import BaseModel
# Create your models here.
MESSAGE_TYPE = [
('text', 'Text'),
('audio', 'Audio'),
('img', 'Image'),
('doc', 'Document'),
('link', 'Link')
]
THREAD_TYPE = [
('individual', 'Individual'),
('group', 'Group')
]
class Thread(BaseModel):
name = models.CharField(max_length=20, null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
thread_type = models.CharField(max_length=20, choices=THREAD_TYPE, default='individual')
class Meta:
db_table = 'in_thread'
verbose_name = 'threads'
verbose_name_plural = 'thread'
ordering = ['-update_date']
class ThreadMember(BaseModel):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE, related_name='thread_member')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='thread_member_user')
is_grp_admin = models.BooleanField(default=False)
def __str__(self):
return f'{self.thread.name} > {self.user}'
class Meta:
db_table = 'in_thread_member'
verbose_name = 'thread members'
verbose_name_plural = 'thread member'
class ChatMessage(BaseModel):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE, related_name='msg_thread')
sender = models.ForeignKey(ThreadMember, on_delete=models.CASCADE, related_name='msg_sender')
message = models.TextField(null=True, blank=True)
sent_at = models.DateTimeField(default=timezone.now())
read_receipt = models.BooleanField(default=False)
msg_type = models.CharField(max_length=20, choices=MESSAGE_TYPE, default='text')
def __str__(self):
return f'{self.sender} > {self.message}'
class Meta:
db_table = 'in_chat_message'
verbose_name = 'chat message'
verbose_name_plural = 'chat messages'
ordering = ['sent_at']
下面是consumers.py
包含 ChatConsumer 类的文件。
聊天/consumer.py
from django.contrib.auth.models import User
import asyncio, json
from channels.consumer import AsyncConsumer
from channels.db import database_sync_to_async
from .models import Thread, ThreadMember, ChatMessage
from .serializers import ChatMessageSerializer
class ChatConsumer(AsyncConsumer):
async def websocket_connect(self, event):
print("connected", event)
try:
kwargs = self.scope['url_route']['kwargs']
thread_id = kwargs.get('thread_id', False)
if thread_id:
thread = await self.get_thread(thread_id)
if thread:
self.chat_room = f'thread_{thread_id}'
await self.channel_layer.group_add(
self.chat_room,
self.channel_name
)
await self.send({
"type": "websocket.accept"
})
else:
await self.send({
"type": "websocket.close"
})
except Exception as e:
print("Error in websocket connection!")
print(e)
async def websocket_receive(self, event):
print("receive", event)
try:
kwargs = self.scope['url_route']['kwargs']
thread_id = kwargs.get('thread_id', False)
thread = await self.get_thread(thread_id)
response = event.get('text', False)
response = json.loads(response)
message = response.get('message', False)
if message:
data, message_saved = await self.save_message(
message, response.get('sender'), thread)
if message_saved:
text = json.dumps(response)
if thread:
await self.channel_layer.group_send(
self.chat_room,
{
"type": "chat_message",
"text": text
}
)
except Exception as e:
print("Error in websocket receive!")
print(e)
async def websocket_disconnect(self, event):
print("disconnected", event)
async def chat_message(self, event):
"""sends the actual message"""
try:
await self.send({
"type": "websocket.send",
"text": event['text']
})
except Exception as e:
print("Error sending messages!")
print(e)
@database_sync_to_async
def get_thread(self, thread_id):
return Thread.objects.get(id=thread_id)
@database_sync_to_async
def save_message(self, message, sender, thread):
try:
sender = ThreadMember.objects.get(
thread=thread.id,
user=User.objects.get(id=sender))
chat = ChatMessage.objects.create(
thread=thread,
sender=sender,
message=message
)
chat.save()
thread.save()
return ChatMessageSerializer(chat).data, True
except Exception as e:
print("Error saving chat!")
print(e)
return False
当我运行 redis 服务器时,我得到以下信息。
C:\Users\rh>cd C:\Program Files\Redis
C:\Program Files\Redis>redis-server redis.windows.conf
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 5.0.10 (1c047b68/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 9628
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
[9628] 11 Dec 12:38:17.011 # Server initialized
[9628] 11 Dec 12:38:17.011 * DB loaded from disk: 0.000 seconds
[9628] 11 Dec 12:38:17.011 * Ready to accept connections
我不知道我在这里做错了什么,我感谢一些帮助。如果需要更多信息,我会改进我的问题。
提前致谢!