最近使用django框架作了一個簡單的聊天機器人demo, 開發的過程當中使用了django自帶的websocket模塊,當使用django框架自帶的wsgi服務去啓動的話,沒有什麼問題。若是要使用uwsgi啓動的話,會報錯:handshake的返回400,也就是客戶端不合法。針對這邊些問題,我去查詢了官方文檔,發現了我問題:html
1. django-websocket 是舊版本的,如今已經沒有人維護了。dwebsocket是新版的,推薦使用dwebsocket;前端
2.使用uwsgi啓動的話,須要原有uwsgi.ini 配置信息下添加兩行配置信息 python
2.1 websocket要使用uwsgi自帶的websocket模塊 mysql
2.2 # 加載項目配置
DJANGO_SETTINGS_MODULE=py_webserver.settings
WEBSOCKET_FACTORY_CLASS="dwebsocket.backends.uwsgi.factory.uWsgiWebSocketFactory"jquery
2.3 啓動腳本以下:uwsgi --ini ./conf/uwsgi.ini --http-websocketsnginx
3.在nginx.conf配置文件中添加幾行配置文件便可解決問題web
整個demo以及配置信息以下:redis
1.項目結構:sql
2.pywebserever項目代碼:django
conf 模塊下的代碼:
mysql.ini
[mysqlsettings]
dbname = webSocket_chatbot
host = 127.0.0.1
port = 3306
uname = root
passwd = mysql
pyweb_log.ini
[logs]
mail_level = ERROR
tofile_level = DEBUG
loggers_level = INFO
uwsgi.ini (uwsgi啓動項目的時候須要的配置文件)
[uwsgi]
#使用nginx鏈接時使用
socket=127.0.0.1:8080
#直接作web服務器使用
#http=127.0.0.1:8080
# 啓動主進程
master=True
pidfile=uwsgi.pid
processes=4
threads=2
#項目目錄
chdir=/home/python/Desktop/test_code/py_webserver
#項目中wsgi.py文件的目錄,相對於項目目錄
wsgi-file=py_webserver/wsgi.py
# 設置日誌目錄
daemonize=logs/uwsgi.log
# 設置緩存
buffer-size=32768
# 當服務器退出的時候自動刪除unix socket 文件和pid 文件
vacuum = true
# 加載項目配置(django + websocket時須要配置的信息)
DJANGO_SETTINGS_MODULE=py_webserver.settings
WEBSOCKET_FACTORY_CLASS="dwebsocket.backends.uwsgi.factory.uWsgiWebSocketFactory"
pywebserver模塊下的代碼:
urls.py:
from django.urls import path, include from django.contrib import admin urlpatterns = [ path('admin/', admin.site.urls), path('', include('webwork.urls')), ]
settings.py:
import os import configparser # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) conf = configparser.ConfigParser() # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'webwork', # 此處註冊應用 ] # Database # https://docs.djangoproject.com/en/1.10/ref/settings/#databases conf.read(BASE_DIR + "/conf/mysql.ini") DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': conf.get("mysqlsettings", "dbname"), "Host":conf.get("mysqlsettings", "host"), "PORT":conf.get("mysqlsettings", "port"), "USER":conf.get("mysqlsettings", "uname"), "PASSWORD":conf.get("mysqlsettings", "passwd"), "CONN_MAX_AGE":600 # 增長鏈接池,保持數據 長鏈接600秒 } } # Logs配置 conf.read(BASE_DIR + "/conf/pyweb_log.ini") LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s', 'datefmt': '%Y-%m-%d %H:%M:%S' }, }, 'filters': { }, 'handlers': { 'mail_admins': { 'level': conf.get("logs", "mail_level"), 'class': 'django.utils.log.AdminEmailHandler', 'formatter': 'standard', }, 'tofile': { 'level': conf.get("logs", "tofile_level"), 'class': 'logging.FileHandler', 'formatter': 'standard', 'filename': os.path.join(BASE_DIR, 'logs/py_web.log'), }, }, 'loggers': { 'django': { 'handlers': ['tofile'], 'level': conf.get("logs", "loggers_level"), 'propagate': True, }, } }
__inint__.py
import pymysql pymysql.install_as_MySQLdb()
template模塊下的代碼:
chat.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Test Django Channels</title> </head> <body> <div style="text-align: center;margin-top: 50px"> <input id="message" type="text" style="width: 300px" placeholder="輸入消息"> <button id="send-message" style="width:80px;margin-left:20px;">發送</button> <button id="close-message" style="width:80px;margin-left:20px;">關閉</button> <button id="connect-message" style="...">登陸鏈接</button> <button id="test" style="...">測試長鏈接</button> </div> <table id="show-message" style="width: 410px;margin: 0 auto;margin-top: 10px"> <tr> <td style="text-align: center; border-bottom:1px dashed #000;"><strong>聊天記錄</strong></td> </tr> </table> </body> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> var socket = new WebSocket('ws://' + window.location.host + '/users/'); socket.onmessage = function (message) { updateLog("機器人", message.data); $("#message").val(""); $("#message").focus(); }; socket.onopen = function () { }; $("#connect-message").click(function () { socket = new WebSocket('ws://' + window.location.host + '/users/'); socket.onmessage = function (message) { updateLog("機器人", message); $("#message").val(""); $("#message").focus(); }; socket.onopen = function () { }; }); $("#close-message").click(function () { updateLog("機器人", "你好,這次服務結束"); socket.close() }); $("#send-message").click(function () { var inputText = $("#message").val(); if (typeof(inputText) == "undefined" || inputText.length < 1) { alert("沒有輸入信息"); } else { var msg = {"text": inputText}; socket.send(JSON.stringify(msg)); updateLog("你", inputText); } }); function updateLog(name, message) { var chat = $("#show-message"); var ele = "<tr><td>" + name + ": " + message + "</td></tr>"; chat.append(ele); } $("#test").click(function () { alert(socket.readyState); }) </script> </html>
webwork模塊下的代碼:
models.py
1 from django.db import models 2 3 # Create your models here. 4 5 class MysqlModel(models.Model): 6 info = models.CharField(max_length=100) 7 save_time = models.DateTimeField() 8 9 class Meta: 10 db_table = "t_chatbot_test"
urls.py
1 from django.urls import include, path 2 from webwork.views import ChatView, user 3 4 urlpatterns = [ 5 path('', ChatView.as_view(), name='chat'), 6 path("users/", user, name="users") 7 8 ]
view.py
1 from django.shortcuts import render 2 from django.views.generic.base import View 3 from .models import MysqlModel 4 import time 5 import uwsgi 6 import json 7 import logging 8 9 10 logger = logging.getLogger("django") 11 # Create your views here. 12 13 class ChatView(View): 14 """加載前端頁面""" 15 @staticmethod 16 def get(request): 17 return render(request, "webwork/chat.html") 18 19 20 def user(request): 21 """接受websocket傳遞過來的信息""" 22 uwsgi.websocket_handshake() 23 uwsgi.websocket_send("你還,很高心爲你服務") 24 while True: 25 msg = uwsgi.websocket_recv() 26 msg = msg.decode() 27 data = json.loads(msg) 28 data_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) 29 talk_words = MysqlModel() 30 talk_words.info = data["text"] 31 talk_words.save_time = data_time 32 try: 33 talk_words.save() 34 talks = MysqlModel.objects.all() 35 except Exception as ret: 36 logger.info("save_data:" + str(ret)) 37 else: 38 for talk in talks: 39 logger.info("talk_info:" + talk.info) 40 uwsgi.websocket_send(data["text"])
requirements.txt 中的依賴包
gi-redis==1.0.0 asgiref==1.0.0 attrs==16.3.0 autobahn==0.17.1 Automat==0.5.0 channels==1.0.3 constantly==15.1.0 daphne==1.0.3 Django==2.0.3 incremental==16.10.1 msgpack-python==0.4.8 PyMySQL==0.8.0 pytz==2018.3 redis==2.10.5 six==1.10.0 Twisted==17.1.0 txaio==2.6.1 txredisapi==1.4.4 uWSGI==2.0.17 zope.interface==4.3.3
3.uwsgi的配置:
3.1 安裝uwsgi pip install uwsgi 3.2 在項目下的conf文件下建立uwsgi.ini文件,具體配置信息如上邊的uwsgi.ini文件所示 3.3 啓動uwsgi:uwsgi --ini ./conf/uwsgi.ini --http-websockets 3.4 查看uwsgi啓動狀態:ps -ef|grep uwsgi 3.5 啓動以後在瀏覽器輸入:127.0.0.1;8080 查看uwsgi服務器的狀態 3.6 中止啓動狀態 uwsgi -- stop uwsgi.pid
# 安裝nginx並驗證是否安裝正確
4.1 下載nginx後放到桌面上,解壓縮 tar zxvf nginx-1.6.3.tar.gz4.2 進入nginx-1.6.3目錄,依次執行如下命令進行安裝 ./configure (--prefix=<path>,nginx 指定安裝根目錄) make sudo make install 4.3 默認安裝到/usr/local/nginx/目錄,進入此目錄 4.4 啓動: sudo sbin/nginx
4.5.查看進程 ps -ef|grep nginx
4.6 瀏覽器檢測ngnix是否啓動:http://127.0.0.1/ 4.7 關閉ngnix服務器:sudo sbin/nginx -s stop
# nginx指向uwsgi
server {
listen 80;
server_name localhost;
location / { #將全部的參數轉到uwsgi下 include uwsgi_params;; #uwsgi的ip與端口 uwsgi_pass 127.0.0.1:8080; # websocket的匹配 proxy_redirect off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
# 註釋掉nginx server下的這幾行配置
#location / {
# root html;
# index index.html index.htm;
# }
STATIC_ROOT="/home/python/Desktop/test_code/django_static/websocket_static"
STATIC_URL = '/static/'
# 將STATICFILES_DIRS的參數註釋掉
4.9.3 收集靜態文件到 STATIC_ROOT 中
4.10 在nginx.conf 的server下增長靜態文件配置
location /static { alias /home/python/Desktop/test_code/django_static/websocket_static; }