channels的接收,转发,断开等函数没有列出,官方文档有很详细的使用方式.本文侧重于部署方面.
pip3 install -U channels
pip3 install channels_redis
Docker 的旧版本被称为 docker,docker.io 或 docker-engine 。如果已安装,请卸载它们:
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt-get update
安装 apt 依赖包,用于通过HTTPS来获取仓库:
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
添加 Docker 的官方 GPG 密钥:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 通过搜索指纹的后8个字符,验证您现在是否拥有带有指纹的密钥。
sudo apt-key fingerprint 0EBFCD88
输出:
pub rsa4096 2017-02-22 [SCEA]
9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
uid [ unknown] Docker Release (CE deb) [email protected]
sub rsa4096 2017-02-22 [S]
使用以下指令设置稳定版仓库
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
安装 Docker Engine-Community
更新 apt 包索引。
sudo apt-get update
安装最新版本的 Docker Engine-Community 和 containerd ,或者转到下一步安装特定版本:
sudo apt-get install docker-ce docker-ce-cli containerd.io
测试 Docker 是否安装成功,输入以下指令,打印出以下信息则安装成功:
sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete Digest: sha256:c3b4ada4687bbaa170745b3e4dd8ac3f194ca95b2d0518b417fb47e5879d9b5f
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
settings.py文件添加设置如下:
ASGI_APPLICATION = 'CSJ_Live.routing.application' # 新建的 asgi 应用
# channels使用redis相关配置
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
添加channels根路由:
在settings同级目录下创建routing.py,内容如下:
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter({
# (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
),
})
settings.py同级目录下创建asgi.py:
"""
ASGI entrypoint. Configures Django and then runs the application
defined in the ASGI_APPLICATION setting.
"""
import os
import django
from channels.routing import get_default_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "项目名.settings")
django.setup()
application = get_default_application()
删除多余文件,只留下__init__.py和views.py
在settings中注册
INSTALLED_APPS = [
.........
'chat', # 新增专用于处理聊天的app
'channels', # 添加channels依赖包
]
创建分路由和视图函数文件:
在chat文件夹内部创建routing.py(分路由文件):
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [ # 路由,指定 websocket 链接对应的 consumer
re_path(r'ws/chat/(?P\w+)/$' , consumers.ChatConsumer),
]
在chat文件夹内部创建consumers.py(视图处理函数文件):
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
# 当 websocket 一链接上以后触发该函数
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# 把当前链接添加到聊天室
# 注意 `group_add` 只支持异步调用,所以这里需要使用`async_to_sync`转换为同步调用
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
# 接受该链接
self.accept()
def disconnect(self, close_code):
# 断开链接是触发该函数
# 将该链接移出聊天室
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
# 前端发送来消息时,通过这个接口传递
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# 发送消息到当前聊天室
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
# 这里的type要在当前类中实现一个相应的函数,
# 下划线或者'.'的都会被Channels转换为下划线处理,
# 所以这里写 'chat.message'也没问题
'type': 'chat_message',
'message': message
}
)
# 接收来自房间组的消息
# 从聊天室拿到消息,后直接将消息返回回去
def chat_message(self, event):
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))
使用docker运行redis:
docker run -p 6379:6379 -d redis:5
让我们确保通道层可以与Redis通信。打开Django shell并运行以下命令:# 注意端口问题
$ python3 manage.py shell
>>> import channels.layers
>>> channel_layer = channels.layers.get_channel_layer()
>>> from asgiref.sync import async_to_sync
>>> async_to_sync(channel_layer.send)('test_channel', {'type': 'hello'})
>>> async_to_sync(channel_layer.receive)('test_channel')
{'type': 'hello'}
pip3 install supervisor
生成配置文件:
echo_supervisord_conf > /etc/supervisord.conf
使用supervisor管理daphne进程
编辑/etc/supervisord.conf加入配置
[program:daphne]
directory=/home/CSJ_Live #项目目录
command=daphne -b 127.0.0.1 -p 8000 --proxy-headers 项目名.asgi:application #启动命令
autostart=true
autorestart=true
stdout_logfile=/tmp/websocket.log #日志
redirect_stderr=true
启动supervisor
supervisord -c /etc/supervisord.conf
启动或者停止daphne
supervisorctl start daphne
supervisorctl stop daphne
更新配置文件
supervisorctl update
重新启动配置的程序
supervisorctl reload
停止全部管理进程
supervisorctl stop all
查看管理进程的状态
supervisorctl status
location ~ /ws/chat {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
web端携带房间号参数发起get请求获取聊天室页面,通过nginx服务器将请求发送给由supervisor管理的daphne,daphne通过application将请求交给Django,Django根路由urls.py处理请求,由根路由转发至分路由chat这个app,分路由chat下的urls.py接收GET请求参数,并将参数交给视图函数views.py,视图函数接收房间号,返回页面并将房间号传入页面(便于websocket连接区分房间).
返回聊天室页面之后在聊天室页面发起websocket请求,同样的经过nginx服务器,nginx将请求转发给django的application,当有websocket请求时,会走channels的根路由routing.py将请求转发至分路由chat下的routing.py,之后将请求交给channels的视图处理函数consumers.py,处理websocket的函数需要继承自WebsocketConsumer.