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 在项目下的con文件下创建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/nginx4.5.查看进程 ps -ef|grep nginx4.6 浏览器检测ngnix是否启动:http://127.0.0.1/
- 4.7 关闭ngnix服务器:sudo sbin/nginx -s stop
# nginx指向uwsgi
- 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;
# }
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;
}