django + nginx + uwsgi + websocket

  最近使用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
4.nginx的配置:
# 安裝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
 4.8.主要配置節點:對於處理http協議的請求,主要配置三個節點
  • http:表示全部的http請求的處理
  • server:監聽端口,綁定服務器
  • location:匹配請求路徑,轉到相應的處理
  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;
        # }

 
  
 4.9.全部的靜態文件都會由nginx處理,不會將請求轉到uwsgi
    4.9.1.先生成靜態文件目錄   此項目我建立靜態文件路徑:/home/python/Desktop/test_code/django_static/websocket_static
    4.9.2.django的settings增長靜態文件配置   

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

相關文章
相關標籤/搜索