kbe使用tornado问题


interface里面有提到可以考虑使用Tornado,作为异步的http服务器。我试用了下,有个地方不是很明白,
tornado最后都要调用
tornado.ioloop.IOLoop.instance().start()
这一步是阻塞的while循环调用,放在interface里面的话,整个进程就停在这一步上面了。

那怎么使用tornado才能达到不影响interface进程?

使用tonado的原因是因为tonado下的http服务是异步的,这能省去我们对http异步访问的处理。
但tonado有一个问题,它使用的是自己的IOLoop来检测socket消息的访问并触发相关的http访问事件,这也就是为什么死循环的原因——脚本层一直不返回,底层自然无法处理自己的事情。
想要在Interfaces下正常使用,必须把socket连接挂到kbe的系统下面(使用KBEngine.registerReadFileDescriptor和KBEngine.registerWriteFileDescriptor),并且根据PollIOLoop的功能继承IOLoop重新写一个自己的looper,这个looper使用KBEngine.timer()定时触发来代替原PollIOLoop里的循环功能,以避免interfaces被阻塞。

耐心的看了下tornado的代码,按照兄台提到的思路,实现如下:
1、tornado\platform下新建个kbe.py,实现一个KBEIOLoop。

import KBEngine
from tornado.ioloop import IOLoop, PollIOLoop
from KBEDebug import *

class _Select(object):
    
    def __init__(self):
        self._active = {}
        self._readable = set()
        self._writeable = set()

    def close(self):
        pass

    def register(self, fd, events):
        INFO_MSG('register fd %d' %(fd))
        if events & IOLoop.READ:
            KBEngine.registerReadFileDescriptor(fd, self.onRecv)
        elif events & IOLoop.WRITE:
            KBEngine.registerWriteFileDescriptor(fd, self.onWrite)
            
        self._active[fd] = events    
        
    def modify(self, fd, events):
        self.unregister(fd)
        self.register(fd, events)

    def unregister(self, fd):
        INFO_MSG('unregister fd %d' %(fd))
        events = self._active.pop(fd)
        if events & IOLoop.READ:
            KBEngine.deregisterReadFileDescriptor(fd)
        elif events & IOLoop.WRITE:
            KBEngine.deregisterWriteFileDescriptor(fd)
    
    def onRecv(self, fd):
        self._readable.add(fd)
    
    def onWrite(self, fd):
        self._writeable.add(fd)
    
    def poll(self, timeout):
        events = {}
        for fd in self._readable:
            events[fd] = events.get(fd, 0) | IOLoop.READ
        for fd in self._writeable:
            events[fd] = events.get(fd, 0) | IOLoop.WRITE
            
        for fd,event in events.items():
            self._readable.discard(fd)
            self._writeable.discard(fd)
        return events.items()

class KBEIOLoop(PollIOLoop):
    def initialize(self, **kwargs):
        super(KBEIOLoop, self).initialize(impl=_Select(), **kwargs)

2、修改下ioloop的配置,把所有平台的实现全部指向KBEIOLoop

@classmethod
    def configurable_default(cls):
        """
        if hasattr(select, "epoll"):
            from tornado.platform.epoll import EPollIOLoop
            return EPollIOLoop
        if hasattr(select, "kqueue"):
            # Python 2.6+ on BSD or Mac
            from tornado.platform.kqueue import KQueueIOLoop
            return KQueueIOLoop
        from tornado.platform.select import SelectIOLoop
        return SelectIOLoop
                """
        from tornado.platform.kbe import KBEIOLoop
        return KBEIOLoop

3、修改下PollIOLoop的start实现,把默认的循环执行改成只执行一次

def start(self):
     ....
     ....
     #执行一次就退出
     self._running = False
finally:
 ....
 ....

这里和kbe结合的tornado实现就完成了。

使用时,我们需要定时器里面一次次的调用start。
测试样例如下:

def handle_response(response):
        if response.error:
            ERROR_MSG("Error: %s" % (response.error))
        else:
            INFO_MSG(response.body)
        
def request():
        """
        """
        
        #非阻塞型
        http_client = tornado.httpclient.AsyncHTTPClient()
        http_client.fetch("http://www.baidu.com/", handle_response)

def onInterfaceAppReady():
        """
        KBEngine method.
        interfaces已经准备好了
        """
        INFO_MSG('onInterfaceAppReady: bootstrapGroupIndex=%s, bootstrapGlobalIndex=%s' % \
         (os.getenv("KBE_BOOTIDX_GROUP"), os.getenv("KBE_BOOTIDX_GLOBAL")))

        request()
        KBEngine.addTimer(0.3, 0, onTornadoIOLoop)
                
def onTornadoIOLoop(timerID):
        """
        """
        KBEngine.delTimer( timerID )
        tornado.ioloop.IOLoop.current().start()
        KBEngine.addTimer(0.3, 0, onTornadoIOLoop)

 

posted on 2018-12-17 21:58  &大飞  阅读(242)  评论(0编辑  收藏  举报

导航