django + channels + nginx + supervisor 部署經(jīng)驗(yàn)總結(jié)

最近在研究python websocket,最一些部署遇到的坑做一下記錄總結(jié),部署channels的前提是其他的https環(huán)境都配置好了,具體的可以看我的另外一篇文章 http://www.itdecent.cn/p/4e3c213d7db0
這里我以其他的環(huán)境都搭建好為前提講解如何使用channels和線上部署
首先我們們先安裝channels,執(zhí)行以下命令

pip3 install channels

會(huì)為我們安裝很多依賴,包括我們后面需要的的 daphne,daphne需要做軟連接,要不然我們后面執(zhí)行daphne命令的時(shí)候會(huì)報(bào)找不到命令的錯(cuò)誤

ln -s /usr/local/python3/bin/daphne  /usr/bin/daphne

這樣我們的channels已經(jīng)安裝好了,下面我們需要做django的配置
首先加載channels,在settings.py
INSTALLED_APPS 中添加channels,如下

INSTALLED_APPS = [
    'channels',
    'chat',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'home.apps.HomeConfig'
]

下面我們?cè)陧?xiàng)目目錄下創(chuàng)建asgi.py配置文件,與wsgi.py同級(jí),配置內(nèi)容如下:

import os
import django
from channels.routing import get_default_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'haomai.settings')
django.setup()
application = get_default_application()

同時(shí)我們創(chuàng)建channels路由配置文件.創(chuàng)建routing.py,配置內(nèi)容如下

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

這是我們把創(chuàng)建個(gè)chat app,主要用來測(cè)試websocket聊天用的,創(chuàng)建命令如下:

python3 manage.py startapp chat

這樣我們就常見了個(gè)chat app,在上面的路由配置文件我們發(fā)現(xiàn) chat.routing.websocket_urlpatterns,目前是找不到的,我們還沒有創(chuàng)建配置,這里我們先創(chuàng)建,在chat 目錄下,同樣創(chuàng)建個(gè)routing.py文件,配置內(nèi)容如下:

from django.conf.urls import url
from django.urls import path
from . import chat

websocket_urlpatterns = [
    path('ws/chat/', chat.ChatConsumer),
]

我們創(chuàng)建服務(wù)端測(cè)試代碼chat.py,內(nèi)容如下:

# 該文件內(nèi)是專門用來寫處理websocket請(qǐng)求的視圖函數(shù)


from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer


consumer_object_list = []

class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        """
        客戶端發(fā)來鏈接請(qǐng)求之后就會(huì)自動(dòng)觸發(fā)
        :param message:
        :return:
        """
        print('連接成功')
        self.accept()  # 向服務(wù)端發(fā)送加密字符串
        # self就是每一個(gè)客戶端對(duì)象
        # 鏈接成功 我就將當(dāng)前對(duì)象放入全局的列表中
        consumer_object_list.append(self)


    def websocket_receive(self, message):
        """
        客戶端向服務(wù)端發(fā)送消息就會(huì)自動(dòng)觸發(fā)
        :param message:內(nèi)部包含客戶端給你發(fā)送的消息  {'type': 'websocket.receive', 'text': '大寶貝'}
        :return:
        """
        print('接收到新消息....')
        print(message)
        # 給客戶端回消息
        # self.send(text_data=message.get('text'))

        # 給列表中所有的對(duì)象都發(fā)送消息
        for obj in consumer_object_list:
            obj.send('服務(wù)器回復(fù)信息..')


    def websocket_disconnect(self, message):
        """
        客戶端主動(dòng)斷開鏈接之后自動(dòng)觸發(fā)
        :param message:
        :return:
        """
        print('斷開鏈接了')
        # 服務(wù)端斷開鏈接 就去列表中刪除對(duì)應(yīng)的客戶端對(duì)象
        consumer_object_list.remove(self)
        raise StopConsumer

我們?cè)趧?chuàng)建個(gè)測(cè)試模板,chat 目錄下創(chuàng)建個(gè)templates目錄,里面創(chuàng)建個(gè)chat.html,先寫上一下websocket連接的測(cè)試代碼內(nèi)容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>chatting</h1>
</body>
<script>
var ws = new WebSocket("ws://127.0.0.1:8080/ws/chat/");
ws.onopen = function(evt) {  //綁定連接事件
  console.log("Connection open ...");
  ws.send("發(fā)送的數(shù)據(jù)");
};

ws.onmessage = function(evt) {//綁定收到消息事件
  console.log( "Received Message: " + evt.data);
};

ws.onclose = function(evt) { //綁定關(guān)閉或斷開連接事件
  console.log("Connection closed.");
};


</script>
</html>

讓后創(chuàng)建路由函數(shù),在chat/view.py 下創(chuàng)建個(gè)index函數(shù),內(nèi)容如下:

from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
    return render(request, 'chat.html')

然后配置 url.py

from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
    path('', views.index)
]

然后再主工程的url.py下include chat的url.py.這里就不演示了,這樣我們啟動(dòng)服務(wù),通過如下命令:

python3 manage.py runserver 

這樣默認(rèn)就會(huì)啟動(dòng)了8000端口的服務(wù),我們直接訪問下面鏈接 http://127.0.0.1:8080/chat/,就會(huì)成功連接到websocket服務(wù),可以打開控制臺(tái)查看log,如下圖:

image.png

看得懂上面的代碼的小伙伴應(yīng)該會(huì)發(fā)現(xiàn)我們的流程很正常,也就是客戶端創(chuàng)建個(gè)連接,連接成功之后,我們客戶端發(fā)送了個(gè)"發(fā)送的數(shù)x據(jù)"字符串,服務(wù)端,收到消息又回復(fù)了一條"服務(wù)器回復(fù)信息..",整個(gè)流程沒有問題,這是在django的服務(wù)器上運(yùn)行的效果,最終我們需要部署上線,以niginx反代理的形式訪問,下面我們開始部署channels,具體步驟
1.上傳代碼到遠(yuǎn)程服務(wù)器
2,安裝依賴環(huán)境,這里直接在本地導(dǎo)出requirements.txt,傳入遠(yuǎn)程服務(wù)器直接運(yùn)行以下

pip3 install -r requirements.txt

3.daphne 啟動(dòng)websocket 服務(wù),這里我們websocket服務(wù)在8001端口啟動(dòng)

daphne -p 8001 -b 127.0.0.1 haomai.asgi:application

4.配置nginx反向代理

 upstream socket.haomai.com {
    server localhost:8001;
}

server {
    listen      8080;
    #server_name www.ziqiangxuetang.com;
    charset     utf-8;
 
    client_max_body_size 75M;
 
    location /static {
        alias /Users/shuaijiguang/Documents/項(xiàng)目倉(cāng)庫(kù)/我的項(xiàng)目/Django學(xué)習(xí)/haomai/haomai/static;
    }
 
      location /ws {
        proxy_pass http://socket.haomai.com;
       
    
    
        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;
    }

    location / {
        uwsgi_pass  127.0.0.1:9999;
        include     /Applications/MAMP/conf/nginx/uwsgi_params;
    }

}

主要是 /ws 配置內(nèi)容,這里我遇到一個(gè)坑,困擾了我很長(zhǎng)時(shí)間,死活websocket都不同,主要是 proxy_pass http://socket.haomai.com;我寫成了 proxy_pass http://socket.haomai.com/;多了個(gè)斜桿,奶奶的,發(fā)現(xiàn)這個(gè)問題腦細(xì)胞死了一大片,重啟nginx,這時(shí)我們發(fā)現(xiàn)js模板文件的ws訪問地址還是本地的127.0.0.1,現(xiàn)在我們部署到了遠(yuǎn)程服務(wù)器了,我們得訪問遠(yuǎn)程的地址,在這里還有一個(gè)非常非常重要的點(diǎn),一開始我寫的8001端口,因?yàn)槲覀僿ebsocket是8001端口,我訪問發(fā)現(xiàn)死活不行,后來想明白了,我們是通過nginx反向代理訪問的,所以說我們的訪問地址的端口是你項(xiàng)目部署的端口,我這里是8080端口,這樣不出意外你就成功了

盡管你現(xiàn)在websocket可以正常使用了,但是還得需要有優(yōu)化的點(diǎn),你可以嘗試下把服務(wù)端的終端關(guān)閉,你在訪問發(fā)現(xiàn)websocket的就出現(xiàn)無(wú)法訪問的現(xiàn)象,這是因?yàn)閐aphne不是常駐線程的,這時(shí)候我們得需要常駐線程,我們就得借助supervisor來管理他了,非常方便,大致步驟分為以下幾步

1.安裝supervisor

pip3 install supervisor

安裝成功之后,我們需要?jiǎng)?chuàng)建軟連接,要不然你運(yùn)行會(huì)發(fā)現(xiàn)找不到命令

ln -s /usr/local/python3/bin/supervisord  /usr/bin/supervisord
2.修改配置文件

配置文件所在目錄 /etc/ supervisord.conf
配置daphne,我這里同時(shí)還配置了uwsgi,這里我附上我完整的配置內(nèi)容

[supervisord]
nodaemon=true
[supervisorctl]

[program:daphne]
directory=/www/wwwroot/haomai/haomai  #項(xiàng)目目錄
command=daphne -b 127.0.0.1 -p 8001 --proxy-headers haomai.asgi:application #啟動(dòng)命令
autostart=true
autorestart=true
stdout_logfile=/www/wwwroot/haomai/haomai/websocket.log  #日志
redirect_stderr=true

[program:uwsgi]
directory=/www/wwwroot/haomai/haomai/haomai  #項(xiàng)目目錄
command=uwsgi --ini uwsgi.ini #啟動(dòng)命令
autostart=true
autorestart=true
stdout_logfile=/www/wwwroot/haomai/haomai/uwsgi.log  #日志
redirect_stderr=true

3.啟動(dòng)supervisor

我們執(zhí)行以下命令開啟 supervisor

supervisord -c /etc/supervisord.conf

這時(shí)候你再測(cè)試,我們關(guān)閉終端,程序正常訪問,一直在常駐線程,到這里你們就完事了,為了更加的完美,我這里將supervisord加入了開機(jī)自啟動(dòng)設(shè)置,要不然每次重啟服務(wù)器之后忘了開啟supervisord,當(dāng)前系統(tǒng)環(huán)境centos7具體的步驟如下

1.創(chuàng)建supervisord.service

我們?cè)谌我馕恢秒S便創(chuàng)建個(gè)這個(gè)文件,運(yùn)行以下命令

touch supervisord.service
vi supervisord.service

2.寫入以下內(nèi)容

#supervisord.service

[Unit] 
Description=Supervisor daemon

[Service] 
Type=forking 
ExecStart=/usr/bin/supervisord -c /etc/supervisord.conf 
ExecStop=/usr/bin/supervisorctl shutdown 
ExecReload=/usr/bin/supervisorctl reload 
KillMode=process 
Restart=on-failure 
RestartSec=42s

[Install] 
WantedBy=multi-user.target

3.將文件拷貝到/usr/lib/systemd/system/

cp supervisord.service /usr/lib/systemd/system/

4.啟動(dòng)服務(wù)

systemctl enable supervisord

5.驗(yàn)證一下是否為開機(jī)啟動(dòng)

systemctl is-enabled supervisord

以上通過之后,重啟服務(wù)器,我們直接訪問發(fā)現(xiàn)都能正常訪問,說明我們的uwsgi和daphne都通過supervisor自動(dòng)起來了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容