django-private-chat2 在线聊天室(django+websocket+nginx)
django-private-chat2是一个Django聊天应用程序,由 Django Channels、Websockets 和 Asyncio 提供支持,已经封装好了一些简单的聊天逻辑:包括 监听连接/断开 websocket,监听发送/接收 websocket消息,获取用户 对话框列表/消息列表等。
github地址:https://github.com/Bearle/django_private_chat2/
如果这个库够用直接按照说明使用即可。我这里因为要扩展实现自己的逻辑,所以自定义了一个模块chat。实现如下:
一.、准备工作:
(1)安装django:pip install django==3.2.20
django版本需要大于3.0,因为需要ASGI的支持;我这里使用的是3.2.20版本。
(2)安装channels:pip install channels==3.0.4
Channels 是一个采用 Django 并将其功能扩展到 HTTP 之外的项目,用于处理 WebSocket。
(3)安装channel-redis:pip install channels-redis==3.3.
1 channels_redis是唯一支持生产使用的由 Django 维护的官方通道层。该层使用 Redis 作为后备存储,支持单服务器和分片配置以及组支持。
(4)安装django-private-chat2:pip install django-private-chat2==1.0.2
(5)安装daphne:pip install daphne==3.0.2
daphne将请求从客户端转发到 Django Channels 或其他ASGI应用,并将响应返回到客户端。
为什么需要使用daphne:
生产环境部署中,使用的是WSGI协议来启动的我们的项目,也就是使用的wsgi.py这个文件来对接的uWSGI服务器。但是channels使用的ASGI协议,在我们使用uWSGI来启动项目的时候,关于ASGI的一些配置他就找不到了。这就导致了所有的websocket请求都是404。
这个问题的解决有两种方案:
1)启用uWSGI来处理所有的http请求,另外开启一个daphne服务来处理websocket请求。这样的好处是按照请求类型分流,两种请求方式互不干扰。(亲测成功)
命令:daphne -p 4001 dingdang168.asgi:application
2)另外一种则是启用daphne服务来处理http和websocket这两种请求。在daphne服务内部他会根据请求类型的不同分开处理。让你既可以访问websocket又可以访问http。
这里采取第一种方法。
二、新增模块chat:
首先在项目根目录下执行:
python manage.py startapp chat
并依次执行以下步骤:
-
将django-private-chat2下所有的目录文件copy到chat模块下。
-
将chat模块下所有对django_private_chat2的引用改为chat。例如:
from django_private_chat2.models
改为from chat.models
from django_private_chat2.serializers
改为from chat.serializers
三、修改settings.py:
(1) 将channels和chat加入到INSTALLED_APPS里,并添加相应配置,如下所示:
INSTALLED_APPS = [
#···,
'daphne', # 放在比较靠前位置
#···,
'channels', # channels应用
'chat', # chat模块
]
(2)设置ASGI应用:
ASGI_APPLICATION = '项目名称.asgi.application'
(3)设置通道层的通信后台:
CHANNEL_LAYERS = {
# 开发环境
# 'default': {
#'BACKEND': 'channels.layers.InMemoryChannelLayer'
# }
# 生产环境
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": ["redis://:password@127.0.0.1:6379/3"], # password改成自己的密码
},
}
四、新建asgi.py文件,与wsgi.py同级:
from django.core.asgi import get_asgi_application
django_asgi_app = get_asgi_application()
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat import urls
application = ProtocolTypeRouter({
# http请求
"http": django_asgi_app,
# websocket请求
"websocket": AuthMiddlewareStack(
URLRouter(urls.websocket_urlpatterns)
),
})
五、nginx配置文件中添加:
server{
# ···
location /chat_ws {
proxy_pass http://127.0.0.1:4001; # asgi的端口
proxy_http_version 1.1;
# 将http连接升级成websocket的连接。
proxy_set_header Upgrade $http_upgrade;
# 启用支持websocket连接
proxy_set_header Connection "upgrade";
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Real-IP $remote_addr;
}
# ···
}
六、重启相关服务:
- 重启nginx:
nginx -s reload #重新加载配置
- 启动asgi服务:
daphne -p 4001 项目名称.asgi:application
七、测试连接:
使用postman测试连接:
状态码101,表示websocket连接成功。
注意:
- 如果是在开发环境下,第五、六步骤可以直接省略,因为使用runserver命令启动时会直接启动asgi,如下图所示提示即表示启动成功。
- ws和wss的关系类似于HTTP和HTTPS。wss 表示在 TLS 之上的 Websocket。WSS提供了加密通信,而WS没有。
- 如果websocket请求需要传递额外的参数,例如token,可以放入请求头的Sec-WebSocket-Protocol中,用","隔开即可如果报错Error during WebSocket handshake: Sent non-empty ‘Sec-WebSocket-Protocol’ header but no response was received,意思是WebSocket握手阶段出错:发送了非空“Sec-WebSocket-Protocol”请求头但是响应中没有此字段
解决方法:
添加响应头Sec-WebSocket-Protocol,值设置为请求头Sec-WebSocket-Protocol的第一个协议
将chat_consumer.py中的await self.accept()改为await self.accept(protocol[0])。
有错误或者遗漏的地方,欢迎指正交流!
参考链接: