Tornado實現一個消息牆。

Tornado對asynchronous http有很好的支持。 因此跟着demo,總結下一個消息牆要怎麼作。html

 

思路: 首先查了下有兩種思路,一種是client pull 一種是server push。 前端

這裏使用的是server pull,技術就是挺流行的comet技術。web

comet大概就是說:我客戶端發送一個請求到服務器端,而後服務器端啓動一個無線循環,將clinet須要的數據放到response中,並繼續刷新,直到整個clinet與server的鏈接斷開。 因此他是一個基於長鏈接的技術。ajax

 

1.第一步 就是要作發送新消息的處理,即一個client發送一個消息後,要廣播通知到全部的client後端

 1 class NewMessage(tornado.web.RequestHandler):
 2     #overwrite post method
 3     def post(self):
 4         #define messages to send
 5         message = {
 6              "id": str(uuid.uuid4()),
 7              "body",self.get_argument("body"),
 8         }
 9         message["html"] =  tornado.escape.to_basestring(
10              self.render_string("message.html",message=message))
11         if self.get_argument("next",None):
12             self.redirect(self.get_arugment("next"))
13         else:
14             self.write(message)
15        #global_message_buffer IS a global var
16  
17         global_message_buffer.new_message([message])

  解釋: message[html] 這部分 是將message傳入模板,而後返回html代碼。服務器

  最關鍵的是,將這個mesage廣播出去。 使用的global_message_buffer. 異步

global_message_buffer = MessageBuffer()async

class MessageBuffe():
    self.__init__(self):
        
        self.waiters = set()
        self.cache = []
        self.cache_size = 10 #define max message cache
    
    self.new_message(self,message):
           #send message to waiters
            for future in self.waiters:
                future.set_result(message)
            #update waiters to empty
            self.waiters = set()
            #update cache
            self.cache.extend(message)
            #check cache size
            if len(self.cache)>self.cache_size:
                self.cache = self.cache[-self.cache_size:]

回來再寫。。tornado

繼續。post

MessagBuffer作的事情,waiters存儲了等待消息的Future.  What is future? Placeholder for an asynchronous result.

cache存儲了已經發送了消息,當新的用戶鏈接進來後,會把cache裏面的消息推送給他。 後面寫的update

new_message作的事情也是很清楚,將新發送的消息給每個在waiters裏的future。而後更新waiters爲空。 (這個地方尚未明天哪裏的緣由)

而後更新cache,若是新加消息後超出了size,那麼就取最新的。

 

2. 新進來的用戶怎麼拿到cache裏的東西呢?

這個仍是簡單,講全部的cache裏的東西,render到主頁就行了

1 class MainHandler(object):
2     def get():
3         self.render("index.html", messages=global_message_buffer.cache)

3. 難點是,如何持續接受新發布的消息呢?也就是說,我在第一步中廣播出去後,全部的client怎麼接受?

在前端的,有個showMessage的方法。這個方法應該是這樣被調用:每從服務器接受到一個response(也就是服務器有消息了),被調用一次,在這個area中添加一個message.html中render後的東西,也就是在第一步中render_string中作的結果。而後調用下滑動的效果-_- 前端這個不會。。

誰調用這個showMessage呢。應該是這樣的流程。 client 進入頁面後,拿到cache中的message後,調用function poll(). 

Poll() 使用ajax向後端請求update我當前的消息,當返回success後,調用showMessage。

挑戰下js

1 function poll(){
2     $.ajax({url:"/message/update,type:"post",datatype:"text",
3                data:"",onSuccess:showMessage,error:error})   
4 }            

前端部分暫時這樣。

那麼這並無解決剛纔的問題,須要的是,我要傳一個東西,讓服務器知道,有了消息要給這個client回覆response。

class UpdateMessage(tornado.web.RequestHanlder):
    @gen.coroutine
    def post(self):
        client_id = self.get_arguments("client_id")
        #將這個clinet加入到message_wait中去,等有了消息就返回給這個Future。
        self.future = global_message_buffer.wait_for_message(client_id)
    
        messages = yield self.future
        self.write(dict(messages=message))

 

 這個wait_for_message

 1 wait_for_message(self,cursor=None):
 2     #考慮若是要給新來的client返回消息的話,須要知道從cache哪一個地方開始返回
 3     #由於是異步的,因此要知道你請求的時候的位置。也就是當前message的id
 4     future = Future()
 5     
 6     if cursor:
 7     
 8         count = 0
 9         
10         for m in reversed(self.cache):
11             #若是到了當前消息了,就再也不超後面拿。數據。
12             if m["id"] == cursor:
13                 break
14             
15             count+=1
16         if count:
17            future.set_result(self.cache[-count:])
18             return future
19      #將這個future加入到須要push到的集合中去。
20      self.waiter.add(future)
21      return future

 

  到這步,咱們總結下整個流程與調用。 首先是用戶載入,載入後調用updateMessage。 這一步參數爲 client_id,初始爲None 直接將其加入waiters。到 self.messages = yield self.future這部等待future被更新,而後調用self.write(). 什麼時候被更新呢? 是在MessageBuffer中  future.set_result(message) 這一步,此時被更新了,而後當即調用self.write().

當一次ajax請求結束後,js中設置timeout來進行下一次鏈接。 直到有response,這次ajax請求才結束。

須要注意的是,只有UpdateMessage的post方法須要@gen.coroutine

基本流程就是這樣了。

相關文章
相關標籤/搜索