Django 同步阻塞模型難以支援高併發即時推播。引入 Node.js + Socket.IO 處理 WebSocket 連線,透過 Redis Pub/Sub 傳遞訊息,實現即時聊天室。架構:瀏覽器 ←WS→ Node.js ←Pub/Sub→ Redis ←Pub→ Django 視圖。
pip install django redis
sudo apt-get install redis-server
# Node.js
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update && sudo apt-get install nodejs
npm install socket.io cookie
# models.py
class Comment(models.Model):
user = models.ForeignKey(User)
text = models.CharField(max_length=255)
# views.py
@login_required
def home(request):
comments = Comment.objects.select_related().all()[:100]
return render(request, 'index.html', {'comments': comments})
@csrf_exempt
def node_api(request):
session = Session.objects.get(session_key=request.POST['sessionid'])
user_id = session.get_decoded()['_auth_user_id']
user = User.objects.get(id=user_id)
Comment.objects.create(user=user, text=request.POST['comment'])
r = redis.StrictRedis()
r.publish('chat', '%s: %s' % (user.username, request.POST['comment']))
return HttpResponse("Everything worked :)")
// nodejs/chat.js
var http = require('http');
var server = http.createServer().listen(4000);
var io = require('socket.io').listen(server);
var cookie = require('cookie');
var querystring = require('querystring');
var redis = require('socket.io/node_modules/redis');
var sub = redis.createClient();
sub.subscribe('chat');
io.configure(function() {
io.set('authorization', function(data, accept) {
if (data.headers.cookie) {
data.cookie = cookie.parse(data.headers.cookie);
accept(null, true);
} else accept('error', false);
});
io.set('log level', 1);
});
io.sockets.on('connection', function(socket) {
sub.on('message', function(channel, msg) {
socket.send(msg);
});
socket.on('send_message', function(msg) {
var data = querystring.stringify({
comment: msg,
sessionid: socket.handshake.cookie['sessionid']
});
var opts = {
host: 'localhost', port: 3000, path: '/node_api',
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': data.length}
};
http.request(opts, function(res) {
res.on('data', function(chunk) {
if (chunk != 'Everything worked :)') console.log('Error:', chunk);
});
}).end(data);
});
});
{% for c in comments %}- {{c.user}}: {{c.text}}
{% endfor %}
python manage.py runserver localhost:3000
node nodejs/chat.js
/node_api 端點。