最近在研究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,如下圖:

看得懂上面的代碼的小伙伴應(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)起來了