asynchronous@tornado 记录二

继续笔记下关于tornado async一些学习知识: 

Tornado支持long-polling,在其提供的demo中推荐参考的例子就是那个chat demo,我这里参考chat demo,提供一个简单的long-polling测试应用“用于统计当前在线人数,并保持最新数据至各个客户端的同步更新”。 

1. 通过URL加一个参数name来模拟在线用户。 
2. Ajax long polling 不只是server端的轮询,client端也需要保持一种请求轮询状态,因为当前的大多数web server都不支持基于单向的HTTP链接的双向通信,我们可以通过websockets,但是这个将HTTP转换成其他形式的协议的操作,目前大多数的浏览器并没有广泛支持,我们可以联想到HTML5 Websockets。我们可以用一种long-lived 链接方式(tornado支持的),用于server和client端之间的数据传输, 类似push-pull的方式,我觉得。 
3. 这次的测试,暂没有提供基于重写on_connection_close来处理offline的用户(直接关闭浏览器),所以用户统计的数量只会随着访问数量的增加而增加  

Comet  

我的测试代码,目前使用的是tornado 1.2.1 

Server端: 
Python2.6代码   收藏代码
  1. import os  
  2. import string  
  3. import time  
  4. import logging  
  5. from datetime import datetime  
  6.   
  7. import tornado.httpserver  
  8. import tornado.ioloop  
  9. import tornado.options  
  10. import tornado.web  
  11. import tornado.httpclient  
  12. from tornado.options import define, options  
  13.   
  14.   
  15. define("port", default=8888, help="run on the given port", type=int)  
  16.   
  17. online = []  
  18. count = 0  
  19.   
  20. class MainHandler(tornado.web.RequestHandler):  
  21.   
  22.     def get(self):  
  23.         self.user = self.get_argument("name", None)  
  24.         self.render("templates/stack_p312a.html", title="Online number testing", c_time=datetime.now(), user=self.user)  
  25.   
  26.   
  27. class LongPollingHandler(tornado.web.RequestHandler):  
  28.   
  29.     @tornado.web.asynchronous  
  30.     def get(self):  
  31.         global online, count  
  32.         self.user = self.get_argument("name", None)  
  33.         if self.user not in online:  
  34.             logging.info("user : " + self.user)  
  35.             online.append(self.user)  
  36.         http = tornado.httpclient.AsyncHTTPClient()  
  37.         appURL = self.request.protocol + "://" + self.request.host  
  38.         http.fetch(appURL + "/internal-polling", self._on_finish)  
  39.   
  40.     '''push to the client'''  
  41.     def _on_finish(self, response):  
  42.         if self.request.connection.stream.closed():  
  43.             return  
  44.         self.write("welcome %s, current online number(s) %s" % (self.user, response.body))  
  45.         self.finish()  
  46.   
  47.     '''  
  48.     def on_connection_close(self):  
  49.         TODO, testing  
  50.     '''  
  51.   
  52. class InternalPollingHandler(tornado.web.RequestHandler):  
  53.   
  54.     '''  
  55.     The internal polling for the new online member which will be counted into  
  56.     the global online list, and then asynchronously push the latest data to the connected client,keep in a long-polling status.  
  57.     '''  
  58.     def get(self):  
  59.         global online, count  
  60.         logging.info("count : " + str(count))  
  61.         logging.info("online : " + str(len(online)))  
  62.         if count != len(online):  
  63.             count += 1  
  64.             self.get()  
  65.         else:  
  66.             self.write(str(count))  
  67.   
  68. def main():  
  69.     tornado.options.parse_command_line()  
  70.   
  71.     settings = {  
  72.         "static_path": os.path.join(os.path.dirname(__file__), "static"),  
  73.     }  
  74.   
  75.     application = tornado.web.Application([  
  76.         (r"/", MainHandler),  
  77.         (r"/long-polling", LongPollingHandler),  
  78.         (r"/internal-polling", InternalPollingHandler),  
  79.         ], **settings  
  80.     )  
  81.     http_server = tornado.httpserver.HTTPServer(application)  
  82.     http_server.listen(options.port)  
  83.     tornado.ioloop.IOLoop.instance().start()  
  84.   
  85.   
  86. if __name__ == "__main__":  
  87.     main()  


Client端(stack_p312a.html): 
Html代码   收藏代码
  1. <html>  
  2. <head>  
  3.     <title>{{ title }}</title>  
  4.     <script type="text/javascript" language="JavaScript" src="{{ static_url("jquery-1.5.1.min.js")}}"></script>  
  5.     <script type='text/javascript' language='JavaScript'>  
  6.         
  7.         function test(){  
  8.                 window.setTimeout(function(){  
  9.                     $.ajax({  
  10.                         url : '/long-polling?name={{ user }}',    
  11.                         success : function(data){  
  12.                             $("#num").text(data);     
  13.                         }  
  14.                     });  
  15.                     test();  
  16.                 }, 5000);  
  17.         }  
  18.     </script>  
  19. </head>  
  20. <body>  
  21.     Current time is {{c_time}}  
  22.     <br>  
  23.     <input type="button" value="Test" onclick="test();"/>  
  24.     <div id="num"></div>  
  25. </body>  
  26. </html>  


简单说明: 

1. 
通过简单的提供 
global online = [] 
global count = 0 
用于用户在线用户的统计 

2. 
http://localhost:8888/?name=a 
http://localhost:8888/?name=b 
通过不同的name来模拟各个用户 

----------------------------------------------- 
对于on_connection_close,the select()-base implements of IOLoop (non-Linux systems) 相关的实现是在tornado 1.2下有修补一些问题,详细可以参考 

changelist : 
https://github.com/facebook/tornado/commit/1221865747ecfde69a0463b9a0d77b3c5b87f320  

但是根据Ben Darnell的建议,并没有在windows下测试过,目前还在学习、讨论中。 
----------------------------------------------- 

在使用tornado async时一些体验: 
1. self.render(), self.redirect()其中已经包含了self.finish()操作,我们在写相关的async callback时候,需要注意这个,不应重复使用self.finish() 
2. RequestHandler.async_callback 这个方法从1.1版本开始已经废除,类似代码(下面)应该不再使用,可以通过AsyncHTTPClient来实现异步请求 

Python2.6代码   收藏代码
  1. @tornado.web.asynchronous  
  2. def get(self):  
  3.     ...  
  4.     self.check_for_last_numbers(callback=self.async_callback(self.on_finish))  
  5.     ...  
  6.   
  7. def check_for_last_numbers(self, callback):  
  8.     ...  


3. self.finish(),self.flush() 可以“简单的理解”为一种server端的push操作,但是实际实现中,建议使用self.finish(),tornado可以基于之前的cursor、session(secure cookie)来重用之前的connection。 


笔记先分享到这里,欢迎大家讨论,共同学习  

你可能感兴趣的:(server,python,application,callback,asynchronous,Numbers)