对于一个小型Django(1.10.3)应用,我希望拥有websockets
,并且我对整个websockets
业务感到陌生。因此,我搜索了一个相对容易理解的django
项目,它启用了websocket
个连接,并且我找到了django-websocket-redis,它看起来很容易使用和部署。
所以我创建了一个非常small example的项目(参见github页面),在那里我可以测试我在阅读django-websocket-redis
文档时学到的东西,我很快就能写出来有用的东西。
如果我使用./manage.py runserver 0.0.0.0:3000
启动应用并转到http://localhost:3000,则该页面可以建立websocket
连接,订阅频道并添加<li>content</li>
每当来自服务器的新消息到达时,在页面上。在django
应用程序中,我启动了一个在websocket
接口上随机播放2到9秒随机字符串的线程。我很满意,我计划看看部署是否像看起来一样简单。
所以我仔细研究了documentation并且几乎制作了副本&amp;粘贴只改变适用于我的东西。我用
创建了一个文件websockets/wsgi_websocket.py
import os
import gevent.socket
import redis.connection
redis.connection.socket = gevent.socket
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "websockets.settings")
from ws4redis.uwsgi_runserver import uWSGIWebsocketServer
application = uWSGIWebsocketServer()
以及带有
的文件websockets/deploy_settings.py
DEBUG = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '::1', '192.168.2.117', 'weby.com']
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'ws4redis',
'websockets',
]
....
WSGI_APPLICATION = 'ws4redis.django_runserver.application'
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(os.path.dirname(ws4redis.__file__), "static"),
]
STATIC_ROOT = os.path.join(BASE_DIR, "deployment", "static")
WEBSOCKET_URL = '/ws/'
WS4REDIS_EXPIRE = 5
WS4REDIS_PREFIX = 'ws'
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_PREFIX = 'session'
...
在index.html
页面中,我按如下方式进行连接:
var ws_url = 'ws://' + window.location.host + '/ws/foobar?subscribe-broadcast&publish-broadcast';
var ws = new WebSocket(ws_url);
...
就像我说的,在DEBUG = True
模式下一切正常。我安装了nginx
(我使用的是Gentoo Linux,因此我启用了emerge nginx
并启用了这些标志:aio http http-cache http2 ipv6 pcre ssl NGINX_MODULES_HTTP=access addition auth_basic autoindex browser charset empty_gif fastcgi geo gzip limit_conn limit_req map memcached proxy referer rewrite scgi split_clients ssi upstream_ip_hash userid uwsgi
)
我将其添加到默认配置
upstream django_upstream {
server 127.0.0.1:17999;
}
upstream websocket_upstream {
server 127.0.0.1:18000;
}
server {
listen 0.0.0.0:80;
server_name weby.com;
access_log /tmp/web.com-access.log;
error_log /tmp/web.com-error.log;
location /static {
alias /home/shaoran/projects/python/websockets/deployment/static;
}
location / {
uwsgi_pass django_upstream;
include /etc/nginx/uwsgi_params;
}
location /ws/ {
proxy_pass http://websocket_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
我已将weby.com
添加到我的/etc/hosts
文件中并解析为127.0.0.1
。我在查找问题后添加的nginx
配置中的最后一行找到this(请参阅tylercb s comment), so I added the
proxy_set_header Host $ host;`line。
uWSGI
实例的启动方式如下:
# exporting DJANGO_SETTINGS_MODULE
export DJANGO_SETTINGS_MODULE=websockets.deploy_settings
# the main django app
uwsgi --socket 127.0.0.1:17999 --wsgi-file websockets/wsgi.py --die-on-term --buffer-size=32768 --workers=5 --master --logto2 /tmp/wsgi_django.log
# the websocket app
uwsgi --socket 127.0.0.1:18000 --wsgi-file websockets/wsgi_websocket.py --die-on-term --gevent 1000 --http-websockets --workers=2 --master --logto2 /tmp/wsgi_websocket.log
现在,当我转到http://weby.com时,该页面并未向我显示随机字符串。我打开控制台(使用谷歌浏览器测试),我看到了这个错误:
WebSocket connection to 'ws://weby.com/ws/foobar?subscribe-broadcast&publish-broadcast' failed: Error during WebSocket handshake: Unexpected response code: 502
在我再次执行的控制台中
ws = new WebSocket("ws://weby.com/ws/foobar?subscribe-broadcast&publish-broadcast")
查看我将在日志文件中获得的错误消息。 django
日志文件没有任何内容。 nginx
错误日志文件说
2016/11/21 01:16:17 [error] 12892#0: *1 upstream prematurely closed connection while reading response header from upstream, client: 127.0.0.1, server: weby.com, request: "GET /ws/foobar?subscribe-broadcast&publish-broadcast HTTP/1.1", upstream: "http://127.0.0.1:18000/ws/foobar?subscribe-broadcast&publish-broadcast", host: "weby.com"
uWSGI
日志文件说
invalid request block size: 21573 (max 4096)...skip
所以我用Google搜索并找到了这个:uwsgi invalid request block size,因此我将-b 32768
选项添加到uwsgi
接口的websocket
命令中。我执行时
ws = new WebSocket("ws://weby.com/ws/foobar?subscribe-broadcast&publish-broadcast")
我没有再收到任何错误消息。 &#34;万岁&#34;我想,但没有消息到达。几秒钟后,我再次得到Error during WebSocket handshake: Unexpected response code: 502
:(
所以我在同一个帖子上添加了--protocol=http
,我收到了一条消息,但仅此而已。 uWSGI
日志只显示
[pid: 13513|app: 0|req: 1/1] 127.0.0.1 () {42 vars in 786 bytes} [Mon Nov 21 00:42:08 2016] GET /ws/foobar?subscribe-broadcast&publish-broadcast => generated 58 bytes in 64418 msecs (HTTP/1.1 101) 4 headers in 178 bytes (3 switches on core 999)
和django
日志说
Subscribed to channels: subscribe-broadcast, publish-broadcast
我重新加载了页面,我终于得到了一条消息,但几秒钟后消息就没有了。我用F5再次重新加载页面,没有,甚至没有像以前那样的第一条消息。
所以我尝试用ws.close()
关闭连接
WebSocket connection to 'ws://weby.com/ws/foobar?subscribe-broadcast&publish-broadcast' failed: One or more reserved bits are on: reserved1 = 1, reserved2 = 0, reserved3 = 1
Websocket connection is broken!
和django
日志说
WebSocketError: unable to receive websocket message
Traceback (most recent call last):
File "/home/shaoran/anaconda/websockets/lib/python2.7/site-packages/ws4redis/wsgi_server.py", line 120, in __call__
recvmsg = RedisMessage(websocket.receive())
File "/home/shaoran/anaconda/websockets/lib/python2.7/site-packages/ws4redis/uwsgi_runserver.py", line 31, in receive
raise WebSocketError(e)
WebSocketError: unable to receive websocket message
我觉得我越接近正确的配置,但在这里我不知道出了什么问题。 nginx
配置似乎没问题,所以它必须是我执行uwsgi
的方式。
事情似乎只是随机工作。我重新加载了页面,但这次我没有做任何事情只是等着看看是否有东西出现。我打开firefox并加载页面,看看这是否是一个chrome问题。使用firefox我完全一样,唯一的区别是firefox在关闭连接时没有显示任何错误消息,但django
日志文件显示WebSocketError
异常。我回到了几分钟前重新加载的chrome页面,令我惊讶的是我发现了5条新消息。我不能重现这种行为,它总是有不同的结果。
我做错了什么?
修改
我想我发现了这个问题。我修改了我的线程:
# inside websockets/views.py
class WS(object):
def __init__(self):
self.counter = 0
def listen_and_replay_to_redis(self):
logger = logging.getLogger("django")
logger.debug(" >> websocket starting thread")
try:
redis_publisher = RedisPublisher(facility='foobar', broadcast=True)
while True:
data = str(uuid.uuid4())
#self.counter += 1
#data = "%s - %s" % (data, self.counter)
redis_publisher.publish_message(RedisMessage(data))
ttw = random.uniform(3, 10)
ttw = 1
logger.debug(" >> websocket thread %s: %s waiting %s seconds" % (datetime.now().strftime("%H:%M:%S"), data, ttw))
time.sleep(ttw)
except Exception as e:
logger.debug(" >> websocket thread error: %s" % e)
logger.debug(" >> websocket thread dying")
obj = WS()
th = threading.Thread(target = obj.listen_and_replay_to_redis)
th.start()
当我在调试模式下启动时,我可以在日志文件中看到该线程正在运行,因为它会生成如下消息:
>> websocket thread 02:09:15: a2d460d4-a660-47ad-845d-1a60a6dddb14 waiting 1 seconds
>> websocket thread 02:09:16: 9313a0e9-8d07-42a7-8cd5-a2d1951d8ba0 waiting 1 seconds
>> websocket thread 02:09:17: fbbfc063-ecb2-4424-a017-1f943d3fdc2d waiting 1 seconds
当我使用wsgi
开始处于生产模式时,我什么也看不见。第一个我发出请求(curl http://weby.com
)日志文件显示
>> websocket starting thread
仅此而已。如果我一直在做curl http://weby.com
我有时会看到&#34; websocket起始线程&#34;消息,有时是其他行。这样就显示RedisPublisher
和redis_publisher.publish_message块或线程被杀死。我在这里阅读了关于启动线程的不同线程,我实现了建议,比如在urls.py
模块中启动线程(因为它只加载一次)或者在uwsgi.py
文件本身,没有似乎工作。在使用uWSGI进行部署时,如何在后台运行线程?