使用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
访问https://:8000/chat/以打开聊天窗口。
您可以在多个浏览器中打开多个窗口,并在它们之间聊天。您可以更改用户名,以及名称应显示的颜色。
还有一些其他示例WebSocket服务器
https://:8000/echo/ - 回复您发送给它的任何内容。
https://: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 |