django + nginx + uwsgi + websocket

Posted payneLi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django + nginx + uwsgi + websocket相关的知识,希望对你有一定的参考价值。

  最近使用django框架做了一个简单的聊天机器人demo, 开发的过程中使用了django自带的websocket模块,当使用django框架自带的wsgi服务去启动的话,没有什么问题。如果要使用uwsgi启动的话,会报错:handshake的返回400,也就是客户端不合法。针对这边些问题,我去查询了官方文档,发现了我问题:

  1. django-websocket 是旧版本的,现在已经没有人维护了。dwebsocket是新版的,推荐使用dwebsocket;

  2.使用uwsgi启动的话,需要原有uwsgi.ini 配置信息下添加两行配置信息 

    2.1 websocket要使用uwsgi自带的websocket模块  

    2.2 # 加载项目配置
      DJANGO_SETTINGS_MODULE=py_webserver.settings
      WEBSOCKET_FACTORY_CLASS="dwebsocket.backends.uwsgi.factory.uWsgiWebSocketFactory"

    2.3  启动脚本如下:uwsgi --ini ./conf/uwsgi.ini --http-websockets

  3.在nginx.conf配置文件中添加几行配置文件即可解决问题

 整个demo以及配置信息如下:

 1.项目结构:

   

2.pywebserever项目代码:

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

 

 

  

 

 

 

   

 

    

 

以上是关于django + nginx + uwsgi + websocket的主要内容,如果未能解决你的问题,请参考以下文章

通过 Nginx 的 uwsgi + django - uwsgi 设置/生成?

转载nginx+uwsgi+django

Django+Nginx+uWSGI = 504 网关超时

uWSGI + Nginx + Django 部署

Django + Uwsgi + Nginx 实现生产环境部署

Django + Uwsgi + Nginx 实现生产环境部署