论坛首页 编程语言技术论坛

python学习笔记 - asynchronous@tornado 学习记录二

浏览 3561 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-03-15   最后修改:2011-03-15
继续笔记下关于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端:
import os
import string
import time
import logging
from datetime import datetime

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.httpclient
from tornado.options import define, options


define("port", default=8888, help="run on the given port", type=int)

online = []
count = 0

class MainHandler(tornado.web.RequestHandler):

    def get(self):
        self.user = self.get_argument("name", None)
        self.render("templates/stack_p312a.html", title="Online number testing", c_time=datetime.now(), user=self.user)


class LongPollingHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    def get(self):
        global online, count
        self.user = self.get_argument("name", None)
        if self.user not in online:
            logging.info("user : " + self.user)
            online.append(self.user)
        http = tornado.httpclient.AsyncHTTPClient()
        appURL = self.request.protocol + "://" + self.request.host
        http.fetch(appURL + "/internal-polling", self._on_finish)

    '''push to the client'''
    def _on_finish(self, response):
        if self.request.connection.stream.closed():
            return
        self.write("welcome %s, current online number(s) %s" % (self.user, response.body))
        self.finish()

    '''
    def on_connection_close(self):
        TODO, testing
    '''

class InternalPollingHandler(tornado.web.RequestHandler):

    '''
    The internal polling for the new online member which will be counted into
    the global online list, and then asynchronously push the latest data to the connected client,keep in a long-polling status.
    '''
    def get(self):
        global online, count
        logging.info("count : " + str(count))
        logging.info("online : " + str(len(online)))
        if count != len(online):
            count += 1
            self.get()
        else:
            self.write(str(count))

def main():
    tornado.options.parse_command_line()

    settings = {
        "static_path": os.path.join(os.path.dirname(__file__), "static"),
    }

    application = tornado.web.Application([
        (r"/", MainHandler),
        (r"/long-polling", LongPollingHandler),
        (r"/internal-polling", InternalPollingHandler),
        ], **settings
    )
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()


if __name__ == "__main__":
    main()



Client端(stack_p312a.html):
<html>
<head>
    <title>{{ title }}</title>
    <script type="text/javascript" language="JavaScript" src="{{ static_url("jquery-1.5.1.min.js")}}"></script>
    <script type='text/javascript' language='JavaScript'>
      
        function test(){
        		window.setTimeout(function(){
        			$.ajax({
        				url : '/long-polling?name={{ user }}',	
        				success : function(data){
        					$("#num").text(data);	
        				}
        			});
        			test();
        		}, 5000);
        }
    </script>
</head>
<body>
    Current time is {{c_time}}
    <br>
    <input type="button" value="Test" onclick="test();"/>
    <div id="num"></div>
</body>
</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来实现异步请求

    @tornado.web.asynchronous
    def get(self):
        ...
        self.check_for_last_numbers(callback=self.async_callback(self.on_finish))
        ...

    def check_for_last_numbers(self, callback):
        ...



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


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







论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics