tornado长轮询的理解

来源:互联网 发布:谷歌拼音输入法 mac 编辑:程序博客网 时间:2024/05/29 07:18


1.什么是长轮询
顾名思义,长轮询就是不停循环请求服务器,获取最新信息。
长轮询分为两类:
1)浏览器以固定时间间隔向服务器发送请求
缺点是轮询频率要足够快,但又不能太频繁,否则当成百上千个客户端不断请求,会使web服务器面临极大压力
2)服务器推送
浏览器和服务器之间保持请求的连接,当服务器数据更新时,向浏览器响应新数据,然后关闭连接,浏览器接收到响应,重新发送请求,服务器保持请求状态,如此循环。
优点是极大减少了web服务器的负载,即时响应,用户体验佳。相对于方法1,客户端制造大量的短而频繁的请求(以及每次处理http头部产生的开销),服务器只有当其接受一个初始请求和再次发送响应时处理连接,大部分时间没有新的数据,连接也不会消耗任何处理器资源。

2.长轮询使用示例
以下示例中,用户可添加P商品(数量为10个)到购物车,当用户添加商品到购物车,或删除购物车的时候,其他用户可以适时看到P商品数量的变化。

1)用户访问主页
显示库存数量,添加/删除购物车操作

复制代码
class DetailHandler(tornado.web.RequestHandler):    def get(self):        self.post()    def post(self):        #商品条码        session=uuid.uuid1()        #查询现有库存        count=self.application.shoppingCart.getInventoryCount()        #显示        self.render("index.html",session=session,count=count)
复制代码

界面如下:

2)主页长轮询商品当前库存

复制代码
class StatusHandler(tornado.web.RequestHandler):    @tornado.web.asynchronous    def get(self):        #注册添加/删除购物车后的回调函数        self.application.shoppingCart.register(self.on_message)    def on_message(self,count):        print str(count)        self.write('{"inventorycount":"%s"}'%count)        self.finish()
复制代码

@tornado.web.asynchronous装饰器表示请求为异步IO类型,服务端没有主动调用finish()方法时,请求连接会一直保持。

register方法注册了在用户添加/删除购物车后,需要调用的回调函数
在用户添加/删除购物车后,服务器会调用所有连接中的请求注册的回调函数,将库存数量响应给所有连接请求,然后关闭每个连接,请求结束。
此时,客户端会循环发起请求,建立连接,当库存变化时,服务器推送新的数据到客户端,结束连接。如此循环

3)添加/删除购物车操作

复制代码
class CartHandler(tornado.web.RequestHandler):    def get(self):        self.post()    def post(self):        action=self.get_argument('action')        session=self.get_argument('session')        if not session:            self.set_status(400)            return        if action=='add':            #添加到购物车            self.application.shoppingCart.moveItemToCart(session)        elif action=='remove':            #删除购物车            self.application.shoppingCart.removeItemFromCart(session)        else:            self.set_status(400)
复制代码

4)具体看一看添加和删除操作系统处理流程

复制代码
class ShoppingCart(object):    totalInventory=10    callbacks=[]    carts={}    def register(self,callback):        self.callbacks.append(callback)    def moveItemToCart(self,session):        if session in self.carts:            return        self.carts[session]=True        self.notifyCallbacks()    def removeItemFromCart(self,session):        if session not in self.carts:            return        del(self.carts[session])        self.notifyCallbacks()    def notifyCallbacks(self):        for c in self.callbacks:            print "**********"            self.callbackHelper(c)        self.callbacks=[]    def callbackHelper(self,callback):        callback(self.getInventoryCount())    def getInventoryCount(self):        return self.totalInventory-len(self.carts)
复制代码

moveItemToCart方法,添加操作时,将商品唯一标识码放入json串,然后将新库存作为参数,调用所有请求的回调,响应各个请求,并关闭连接。

removeItemFromCart方法,删除操作时,将商品唯一标识码清除,同样调用各回调,通知客户端。

5)客户端长轮询代码如下

复制代码
$(document).ready(function() {    document.session = $('#session').val();    setTimeout(requestInventory, 100);    $('#add-button').click(function(event) {        jQuery.ajax({            url: 'http://localhost:9999/cart',            type: 'POST',            data: {                session: document.session,                action: 'add'            },            dataType: 'json',            beforeSend: function(xhr, settings) {                $(event.target).attr('disabled', 'disabled');            },            success: function(data, status, xhr) {                $('#add-to-cart').hide();                $('#remove-from-cart').show();                $(event.target).removeAttr('disabled');            }        });    });    $('#remove-button').click(function(event) {        jQuery.ajax({            url: 'http://localhost:9999/cart',            type: 'POST',            data: {                session: document.session,                action: 'remove'            },            dataType: 'json',            beforeSend: function(xhr, settings) {                $(event.target).attr('disabled', 'disabled');            },            success: function(data, status, xhr) {                $('#remove-from-cart').hide();                $('#add-to-cart').show();                $(event.target).removeAttr('disabled');            }        });    });});function requestInventory() {    jQuery.getJSON('http://localhost/status', {session: document.session},        function(data, status, xhr) {            $('#count').html(data.inventorycount);            setTimeout(requestInventory, 0);        }    );}
复制代码

6)运行结果
打开多个客户端,当做添加/删除操作时,可以观察到库存数量会实时变动。

3.长轮询的缺陷
上面有提到过长轮询的优势,经过上面的示例,我们可以明白长轮询存在的一些缺陷。
1)所有客户端请求同时关闭,同时打开,在库存变化的时候,服务器会受到猛烈的攻击
2)长轮询保持了连接请求,很多浏览器限制了对于服务器的并发请求数量,所以一直占用连接,会导致其他的请求如下载受到限制。
3)浏览器请求超时是由浏览器控制的。

0 0
原创粉丝点击