cundi / blog

push issues as a blog
2 stars 0 forks source link

[翻译]Django中websocket的实现 #1

Open cundi opened 9 years ago

cundi commented 9 years ago

原始链接:http://www.aptuz.com/blog/implementing-websockets-with-django/

Users now demand information as soon as it's available. If you have to refresh the page to get new information, it's already too late. Luckily, a protocol supported by all modern browsers allows for direct data exchange: WebSockets.

According to Google, WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply. In plain words: There is a persistent connection between the client and the server and both parties can start sending data at any time.

We use Django as the backbone for most of our projects, recently we attempted at developing a multiplayer javascript gaming platform. In the course of that project WebSockets played a vital role in communication between players and the server and also among the other players. To achieve this we used a library called django-websockets-redis developed by Jacob Rief. I would like to give you a few details on how we used this library to achieve our goal.

In django-websockets-redis, there are four types in which we can subscribe for asynchronous notifications from the server,

subscribe-broadcast

subscribe-user

subscribe-group

subscribe-session

In our project we mainly used subscribe-broadcast and subscribe-user. Our use case is as follows, when a player logs in he/she can choose a game from a multitude of games and then the player can send a request to another player asking them to play the game with him/her. If the other player accepts the request, then a game session is created and the players are redirected to the game page and play their game in peace. The following are the snippets of code which deals with websockets,

Firstly when a player logs in he/she is subscribed to a ‘notifications’ channel which is of type ‘broadcast’. The purpose of this channel is that, whenever the site administrator wants to issue a public notification which is to be viewed by all the members then the administrator can publish a message into the ‘notifications’ channel, it is immediately broadcasted to all the players who are listening this particular channel.

Client Side var ws4redis = WS4Redis({ uri: "ws://127.0.0.1/ws/notifications?subscribe-broadcast", receive_message: function(message){ console.log(message); }, heartbeat_msg: '--heartbeat--'}); Server Side from ws4redis.publisher import RedisPublisher# the message is broadcasted to everyone listening to ‘notifications’ channelredis_publisher = RedisPublisher(facility='notifications', broadcast=True) message = RedisMessage('There will be a site maintenance from 01:00 to 04:00 tomorrow.') redis_publisher.publish_message(message) Secondly when a player requests to play a game with another player and the other player accepts the request, a game session with an unique gameID is created and the players are redirected to the game page and in the game page they are subscribed to the ‘gameID’ channel of type ‘user’ to receive websocket notifications.

Client Side

var ws4redis = WS4Redis({ uri: "ws://127.0.0.1/ws/"+gameID+"?subscribe-user", receive_message: function(message){ console.log(message); }, heartbeat_msg: '--heartbeat--'});

Server Side

from ws4redis.publisher import RedisPublisher# playerID of the player who published the messageplayerID = request.GET[‘playerID’]

rest of the players excluding the player who published the messageremaining_players = players.remove(playerID)

gameID = request.GET[‘gameID’]# the message is sent to the players subscribed to a particular gameID channelredis_publisher = RedisPublisher(facility=gameID, users=players)

message = RedisMessage(gameData) redis_publisher.publish_message(message) In deployment, we used gunicorn to deploy the django webserver and used uWSGI to deploy the websocket server at the back of nginx.

Nginx Snippet

upstream webserver { server 0.0.0.0:8000; }upstream websocket { server 0.0.0.0:8001; }map $http_upgrade $connection_upgrade { default upgrade; "" close; }server { listen 80; server_name 127.0.0.1; # substitute your machine's IP address or FQDN charset utf-8; # websocket path location /ws { proxy_pass http://websocket; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } # webserver path location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass http://webserver; proxy_redirect off;
} }

Gunicorn Snippet

gunicorn DJANGO_WSGI_MODULE:application --workers 3 --bind=0.0.0.0:8000

uWSGI Snippet

INI File [uwsgi] umask = 002 virtualenv = chdir = module = :application no-orphans = true die-on-term = true memory-report = true http-socket = 0.0.0.0:8001 http-websockets = true gevent = 1000 threads = 1 processes = 1 master = true pidfile = /tmp/uwsgi-emperor.pid daemonize = logto = stop = /tmp/uwsgi-emperor.pid

to start uwsgi server

uwsgi --ini Finally you can have a look at our attempt at developing the multiplayer gaming platform in github aptuz/funguilds.