在带有Django频道的房间中获取连接用户的列表
我们如何通过Django频道将连接用户的列表列入频道内的房间?我知道这个问题已经很多次,但是老实说,我在互联网上都搜寻了,我很惊讶没有传统的方式。根据 this 答案,Django频道已经避免了这种情况,但是,还有另一个软件包, django存在,可以满足它。
到目前为止,我有一个非常简单的应用程序,其中Websocket广播了从每个客户端发送的数据,因此房间中的每个连接客户端都会收到该数据。我也有一个简单的身份验证,因此只有身份验证的用户才能连接。这就是我所拥有的:
routing.py
application = ProtocolTypeRouter(
{
"websocket": TokenAuthMiddleware(
URLRouter([
re_path(r'ws/(?P<room_name>\w+)/$', consumers.MyConsumer.as_asgi()),
])
)
}
)
middleware.py
class TokenAuthMiddleware(BaseMiddleware):
def __init__(self, inner):
super().__init__(inner)
async def __call__(self, scope, receive, send):
# Get the token
token = parse_qs(scope["query_string"].decode("utf8"))["token"][0]
#custom code to validate token
if token_is_valid:
# assign scope['user'] = user
else:
scope['user'] = AnonymousUser()
return await super().__call__(scope, receive, send)
commuter.py
def connect(self):
user = self.scope["user"]
if user.is_anonymous:
self.close()
else:
self.room_name = self.scope['url_route']['kwargs']['room_name']
async_to_sync(self.channel_layer.group_add) (
self.room_name,
self.channel_name
)
self.accept() # WHAT CAN I DO HERE TO RETURN THE LIST OF CONNECTED USERS IN THIS SAME ROOM NAME?
def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard) (
self.room_name,
self.channel_name
)
关于我在消费者中的评论
我想到了两个想法(第一个想法可能比第二个好):
使用内存通道层来跟踪用户连接/断开连接的用户。这样一来,我希望将用户列表回复回到Connect上已建立的连接(即客户端)。如何完成?
另外,我已经看到了可以使用数据库跟踪用户活动(即在线/离线等)的示例。对我来说,这不是一个完全最佳的解决方案,因为我不想查询此类活动的数据库。
如果有一个更好的解决方案,我全都是耳朵,但是从本质上讲,这怎么办?
How can we get the list of connected users to a room within channel with Django Channels? I know this question has been asked many times, but honestly, I've scoured all over the internet and I'm very surprised there isn't a conventional way of doing so. According to this answer, Django Channels have avoided this scenario, however, there is another package, Django Presence, that can cater for it.
So far, I've got a very simple app where a websocket broadcasts data that is sent from each client, so that each connected client in a room receives that data. I've also got a simple authentication so that only authenticated users can connect. This is what I have:
routing.py
application = ProtocolTypeRouter(
{
"websocket": TokenAuthMiddleware(
URLRouter([
re_path(r'ws/(?P<room_name>\w+)/
middleware.py
class TokenAuthMiddleware(BaseMiddleware):
def __init__(self, inner):
super().__init__(inner)
async def __call__(self, scope, receive, send):
# Get the token
token = parse_qs(scope["query_string"].decode("utf8"))["token"][0]
#custom code to validate token
if token_is_valid:
# assign scope['user'] = user
else:
scope['user'] = AnonymousUser()
return await super().__call__(scope, receive, send)
consumer.py
def connect(self):
user = self.scope["user"]
if user.is_anonymous:
self.close()
else:
self.room_name = self.scope['url_route']['kwargs']['room_name']
async_to_sync(self.channel_layer.group_add) (
self.room_name,
self.channel_name
)
self.accept() # WHAT CAN I DO HERE TO RETURN THE LIST OF CONNECTED USERS IN THIS SAME ROOM NAME?
def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard) (
self.room_name,
self.channel_name
)
with regards to my comment in the consumers
, two ideas come to my mind (the first probably better than the second):
-
Make use of the in memory channel layer to track which users connect/disconnect. In doing so, I'd like for the list of users to be replied back to the established connection (i.e. the client) on connect. How can this be done?
-
Alternatively, I've seen examples where we can track a user's activity (i.e. online/away/offline etc) using the database. This for me isn't a totally optimal solution since I don't want to be querying the database for that sort of activity.
If there is a better solution, I'm all ears, but essentially, how can this be done?
, consumers.MyConsumer.as_asgi()),
])
)
}
)
middleware.py
consumer.py
with regards to my comment in the consumers
, two ideas come to my mind (the first probably better than the second):
Make use of the in memory channel layer to track which users connect/disconnect. In doing so, I'd like for the list of users to be replied back to the established connection (i.e. the client) on connect. How can this be done?
Alternatively, I've seen examples where we can track a user's activity (i.e. online/away/offline etc) using the database. This for me isn't a totally optimal solution since I don't want to be querying the database for that sort of activity.
If there is a better solution, I'm all ears, but essentially, how can this be done?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
虽然我尚未在生产中测试它,但我能够通过将
defaultDict
作为类属性添加到我的消费者的类属性,并跟踪内存中相关连接信息。对于我的用例,我只关心用户的计数:在您的情况下,您想跟踪连接用户的列表。为此,您将修改
room_connection_counts
defaultDict
以返回list
或set> set
默认情况下。例如,在班级主体中,您将使用:在连接方法中,您将使用:
在断开连接中:
对于问题的第二部分,您想将连接用户的列表发送回频道组。您可以通过在连接方法中添加以下内容来执行此操作:
并在您的消费者中定义
user_connect
方法,请通过group_send方法调用:您将遵循相同的模式以在disconnect上发送消息为出色地。
当然,您必须小心不要使内存超载,因此您可能希望对单个房间存储多少用户在将其传递给DB之前,对其进行限制。
我希望这会有所帮助!我很想听听其他人在这种方法中看到的好处/缺点,而不是打电话给数据库。
While I haven't tested it in production, I was able to handle this by adding a
defaultdict
as a class attribute to my consumer, and keeping track of the relevant connection information in-memory. For my use case, I only was concerned with the count of users:In your case, you want to keep track of a list of connected users. To do that, you would modify the
room_connection_counts
defaultdict
to return alist
orset
of users by default. For example, in the class body, you would use:And in the connect method, you would use:
And in the disconnect:
For the second part of your question, you want to send the list of connected users back to the channel group. You can do that by adding the following in your connect method:
And define a
user_connect
method in your consumer, to be called by the group_send method:You would follow the same pattern to send a message on disconnect as well.
Of course, you have to be careful not to overload your memory, so you might wish to put limits on how many users can be stored for a single room before passing it along to the db.
I hope this helps! I'm curious to hear the benefits/drawbacks others see in this approach vs. making calls out to the db.
如果使用RedisChannellayer,请使用下一个方法扩展它们:
在设置中定义:
在消费者内使用:
If you use RedisChannelLayer, extend them with next method:
define in settings:
use inside the consumer:
您可以创建一个模型
chat
,该将表示此ROOM
然后,当任何用户连接到Websocket时,将其添加到
CHAT的
field fieldusers_online
:当用户与Websocket的用户断开连接时,用户用户
pk
从users_online
字段You can create a model
chat
which will represent thisroom
then when any user connects to the websocket, add him to
Chat's
fieldusers_online
:When user disconnets from your websocket,
remove
userspk
from theusers_online
field