使用Tornado和Redis構建簡易聊天室

[size=medium] Tornado是一個異步Python框架,最初由FriendFeed發起並開源,目前由Facebook維護,非常適合實時做Web應用。
Redis是一個NoSQL數據庫,常用於做緩存系統,這裏主要用到了它的Pub/Sub功能。即當一個用戶發送一條消息時,所有的用戶都會收到消息。
關於什麼是Ajax長輪詢(Comet)不再介紹

我是參照the5fire的一篇博客來組織項目源碼的:[url]http://www.the5fire.com/tornado-mvc-helloworld-2.html[/url]

當Tornado收到瀏覽器的消息時,將這條消息publish到Redis裏,所有的subscriber就都會收到通知。[/size]

def post(self) : #接受POST請求
name = self.get_secure_cookie('name')
msg = self.get_argument('msg', '')

if name == '':
name = 'Anonymous'

data=json_encode({'name':name, 'msg':msg})
c.publish('test_channel', data) #收到將消息publish到Redis
self.write(json_encode({'result':True}));
self.finish();

[size=medium]

將Tornado超時時間設置爲60s,如果在60s內,收到了Redis的消息,就把消息發送給瀏覽器;如果60s超時了,則發送一條msg爲空的消息給瀏覽器。[/size]

import time
import tornado.web
import tornado.gen
import tornadoredis
from tornado.escape import json_encode
from model.entity import Entity

class LongPollingHandler(tornado.web.RequestHandler):

def initialize(self):
self.client = tornadoredis.Client()
self.client.connect() #連接到Redis

@tornado.web.asynchronous
def get(self):
self.get_data()

@tornado.web.asynchronous
def post(self):
self.get_data()

@tornado.gen.engine
def subscribe(self): #訂閱Redis的消息
yield tornado.gen.Task(self.client.subscribe, 'test_channel')
self.client.listen(self.on_message)


def get_data(self):
if self.request.connection.stream.closed():
return

self.subscribe()

num = 60 #設置超時時間爲60s
tornado.ioloop.IOLoop.instance().add_timeout(
time.time()+num,
lambda: self.on_timeout(num)
)


def on_timeout(self, num):
self.send_data(json_encode({'name':'', 'msg':''}))
if (self.client.connection.connected()):
self.client.disconnect()

def send_data(self, data): #發送響應
if self.request.connection.stream.closed():
return

self.set_header('Content-Type', 'application/json; charset=UTF-8')
self.write(data)
self.finish()


def on_message(self, msg): #收到了Redis的消息
if (msg.kind == 'message'):
self.send_data(str(msg.body))
elif (msg.kind == 'unsubscribe'):
self.client.disconnect()

def on_finish(self):
if (self.client.subscribed):
self.client.unsubscribe('test_channel');


[size=medium]

瀏覽器JavaScript代碼如下,每當服務器返回結果以後,就立即再發送一個請求到服務器。因爲空閒的時候,服務器要等60s纔會有響應,所以這並不會消耗很多資源。[/size]

var updater = {
poll: function(){
$.ajax({url: "/longpolling",
type: "POST",
dataType: "json",
success: updater.onSuccess,
error: updater.onError});
},
onSuccess: function(data, dataStatus){
try{
if (data.msg != "") {
$("p").append(data.name+": " + data.msg + "<br />");
}
}
catch(e){
updater.onError(e);
return;
}
updater.poll(); //收到響應後立即再發一條請求
},
onError: function(e){
if (e.message)
console.log("Poll Error" + e.message);
else
console.log(e);
}
};

updater.poll();


[size=medium]全部代碼參見我的git:[url]https://github.com/wylazy/tornado-chat[/url][/size]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章