zoukankan      html  css  js  c++  java
  • tornado长轮询


    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)浏览器请求超时是由浏览器控制的。

     参考资料:http://docs.pythontab.com/tornado/introduction-to-tornado/ch5.html

  • 相关阅读:
    原创:一段利用C#2005操作FOXPRO表的函数
    对VS2005的TreeView控件的困惑(或者是建议吧)
    哎呀!实在是巨烦“驱动之家”这个网站!!
    一种Server Application Unavailable错误的解决办法:
    Win7系统下解决VB6.0鼠标滚轮支持
    [分享]关于水晶报表导出到PDF格式的一个注意事项
    mmsPlayer, for android,ios ,wince,windows,wm等
    C#汉字生成拼音
    使用C#读写文件
    如何使PNG图片在IE浏览器实现透明效果
  • 原文地址:https://www.cnblogs.com/shijingjing07/p/6558217.html
Copyright © 2011-2022 走看看