如何使用nginx真正部署Django websocket应用程序?

时间:2016-11-21 01:23:06

标签: python django nginx websocket uwsgi

对于一个小型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进行部署时,如何在后台运行线程?

0 个答案:

没有答案