Django+channels+supervisor+daphne+docker+redis+nginx部署web聊天室

参考文档:处理函数参照官方文档

channels的接收,转发,断开等函数没有列出,官方文档有很详细的使用方式.本文侧重于部署方面.

安装 channels

pip3 install -U channels

安装channels_redis

pip3 install channels_redis

安装docker

Docker 的旧版本被称为 docker,docker.io 或 docker-engine 。如果已安装,请卸载它们:

sudo apt-get remove docker docker-engine docker.io containerd runc

更新 apt 包索引:

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文件添加设置如下:

channels的相关设置

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,内容如下:

channels根路由文件

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
        )
    ),
})

添加asgi文件:

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()

创建聊天室app:chat:

删除多余文件,只留下__init__.py和views.py
在settings中注册

INSTALLED_APPS = [
   .........
    'chat',  # 新增专用于处理聊天的app
    'channels',  # 添加channels依赖包
]

创建分路由和视图函数文件:
在chat文件夹内部创建routing.py(分路由文件):

chat/routing.py

from django.urls import re_path

from . import consumers

websocket_urlpatterns = [  # 路由,指定 websocket 链接对应的 consumer
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]

在chat文件夹内部创建consumers.py(视图处理函数文件):

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'}

安装supervisor:

apt-get install supervisor

进去conf目录写配置文件:

cd /etc/supervisor/conf.d/
vim test.conf

添加以下配置

[fcgi-program:asgi]
socket=tcp://127.0.0.1:8000
directory=/home/CSJ_LIVE_TEST/CSJ_LIVE
command=daphne -u /run/daphne/daphne%(process_num)d.sock --fd 0 --access-log - --proxy-headers CSJ_LIVE.asgi:application
numprocs=5
process_name=asgi%(process_num)d
autostart=true
autorestart=true
stdout_logfile=/tmp/websocket.log
redirect_stderr=true

为超级用户配置文件中引用的套接字创建运行目录(不然会导致启动失败而一直重启)
参考上面command参数的sock路径

sudo mkdir /run/daphne/

检查配置文件是否可用并启用

sudo supervisorctl reread
sudo supervisorctl update

其他命令

启动或者停止

supervisorctl start all
supervisorctl stop all

更新配置文件

supervisorctl update

重新启动配置的程序

supervisorctl reload

停止全部管理进程

supervisorctl stop all

查看管理进程的状态

supervisorctl status

配置nginx

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.

猜你喜欢

转载自blog.csdn.net/weixin_44784018/article/details/105504956