最近使用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.pidprocesses=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;
}