使用gevent-websocket的简单WebSocket服务器,适用于Django
项目描述
简介
WSGI被认为与WebSocket大部分不兼容,但这并不一定正确。正如gevent-websocket所示,您可以使用gunicorn轻松运行WSGI/WebSocket服务器。
然而,能够将WebSocket服务器与Django集成将非常理想。也就是说,不仅能够访问Django模型,还能够访问整个Django基础设施。虽然使用模板渲染可能没有太多意义,但能够访问来自django项目的中间件和身份验证功能将非常方便。并且能够使用django表单处理来验证用户输入,这意味着您可以编写更健壮的代码。
此模块允许您做到这一点。您可以在正常WSGI服务器旁边运行一个服务器,该服务器侦听WebSocket连接请求。传入的连接最初由Django URL路由设施(但使用单独的urlconf)处理,并应用Django中间件。然后,您的WebSocket视图函数被调用,从那时起处理连接。
沙箱
进入test_project
$ virtualenv . . bin/activate pip install -r requirements.txt
您需要打开两个shell
./project/manage.py runserver 0.0.0.0 gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" project.wsgi:websocket -b 0.0.0.0:8001
访问http://localhost:8000/chat/以打开聊天窗口。
您可以在多个浏览器中打开多个窗口,并在它们之间聊天。您可以更改用户名,以及名称应显示的颜色。
还有一些其他示例WebSocket服务器
http://localhost:8000/echo/ - 回复您发送给它的任何内容。
http://localhost:8000/ping/ - 如果您发送“ping”,则响应“pong”,反之亦然。否则,不要进行任何响应。
安装
使用以下命令从PyPI安装:
$ pip install django-gevent-websocket
它将安装其各种依赖项
django
gevent-websocket
gunicorn
用法
设置
将以下内容添加到您的 $PROJECT_DIR/wsgi.py
from django_websocket.wsgi import get_wsgi_websocket_application websocket = get_wsgi_websocket_application()
将以下内容添加到您的 $PROJECT_DIR/settings.py
WEBSOCKET_URLCONF = '<path-to-websocket-urlconf>'
建议的urlconf可以是 $PROJECT_DIR/websocket_urls.py。
然后您需要将您的WebSocket urls添加到该文件中:它们将与您的正常urls非常相似,但只会包含应该接受WebSocket连接的端点
from django.conf.urls import patterns, url urlpatterns = patterns('', url(r'^chat/$', 'path.to.view', name='chat'), )
如果您想使用 {% websocket_url %} 获取指向正确WebSocket服务器和端口的绝对urls,那么您需要设置
视图
WebSocket视图函数看起来非常像普通的django视图函数,实际上,它是由常规django请求周期调用的,但它不返回HttpResponse对象。相反,您从请求中获取websocket对象,并从它那里 .receive() 数据,或者 .send(data) 到它。
为了使事情变得简单,您可以使用装饰器来包装您的视图
from django_websocket.decorators import websocket @websocket def view_function(request, websocket, *args, **kwargs): # do stuff here.
此装饰器将websocket作为视图的第二个参数添加:您仍然有请求对象,允许您进行权限检查等操作。如果您愿意,仍然可以使用任何常规django装饰器。
视图中的异步性
WebSocket视图应该监听来自客户端需要处理的数据,但可能还需要监听来自另一个源的数据,这些数据将被发送回客户端。
这样做的一个简单方法是使用 select.select 函数,并使用它来编写非阻塞代码,等待信号继续。通常,您将想要循环,直到WebSocket关闭,然后等待信号
from select import select from django_websocket.decorators import websocket @websocket def view_function(request, websocket): ws_sock = websocket.handler.socket.fileno() other_sock = ... # other socket to listen on. while not websocket.closed(): fd = select([ws_sock, other_sock], [], [])[0][0] if fd == ws_sock: data = websocket.receive() # Deal with incoming data. else: ## fd == other_sock: # Deal with data from the other source websocket.send(message)
一个“其他”来源可能是一个Redis PubSub订阅,它允许您订阅频道,并在这些频道中的任何一个上有新数据可用时通知您。
来自 django_websocket.servers.chat
from select import select import redis from ..decorators import websocket @websocket def chat(request, websocket, *args, **kwargs): conn = redis.StrictRedis() subs = conn.pubsub() subs.subscribe('CHAT') def incoming(): data = websocket.receive() if data: conn.publish('CHAT', data) def outgoing(): msg_type, channel, message = subs.parse_response() if msg_type == 'message': websocket.send(message) sockets = { websocket.handler.socket.fileno(): incoming, subs.connection._sock.fileno(): outgoing } while not websocket.closed: fd = select(sockets.keys(), [], [])[0][0] sockets[fd]()
模板标签
因为您应该为WebSocket视图有不同的urlconf,所以您需要使用一个稍微不同的模板标签来访问WebSocket urls
{% load websockets %} <script> var ws = new WebSocket("{% websocket_url 'urlname' %}"); // Do something with your shiny new WebSocket! // ws.send('foo'); </script>
启动服务器
您需要单独启动 geventwebsocket 服务器:没有django管理命令(这反映了run_gunicorn的弃用)
gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" $PROJECT_DIR.wsgi:websocket -b 127.0.0.1:8001
注意这运行在不同于您的django开发服务器(或生产中的gunicorn)的端口上。在生产中,您可能将两者都放在nginx代理或类似的后面。如果您想这样做,您可能需要进行一些复杂的设置,以便WebSocket连接的分离可以正常工作。我做了类似以下的事情
server { listen 80; proxy_set_header Host $host; location /static/ { alias ... } if ($http_upgrade) { rewrite ^(.*)$ /__ws__/$1 break; } location /__ws__/ { proxy_pass http://127.0.0.1:8001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; rewrite ^/__ws__/(.*)$ $1 break; return; } location / { proxy_pass http://127.0.0.1:8000; } }
注意双重的重写。当存在Upgrade头时,它重写url,然后重写回来,以便url在django内部匹配。
项目详细信息
django-gevent-websocket-0.2.0.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 707b0ffb2c3b1ddb33469d8c76219852b8fb3b50e9eb7749e739956ca468f21a |
|
MD5 | d3538c61032be63edf69fcaf17e3ab50 |
|
BLAKE2b-256 | e2ec5865e184153f0f14d71f4b189253505635b9ee15be0dd4c19d4167212c19 |