2011-03-03 51 views
2

我想实现一个基于龙卷风的Web服务,它可以为用户提供数据库查询服务。我使用pyodbc模块连接到数据库并执行查询。实际上,我发现打印查询结果需要很长时间。也就是说,如果我用下面的代码打印查询结果与Tornado和pyodbc的异步调用

while 1: 
    data = cursor.fetchone() 
    if not data: break 
    self.write(data + '\n') 
    self.flush() 

和SQL命令是一样的东西

select * from <a large dummy table> 

龙卷风,直到循环结束不打印查询结果。这需要很长时间。

我想使用龙卷风的非阻塞异步功能,使其他用户也能够使用Web服务,即使打印当前用户的查询请求的循环尚未完成。

所以我写的东西,如:

@tornado.web.asynchronous 
def get(self): 
    try: 
     cnxn = pyodbc.connect(self.server, self.driver, self.table, self.uid, self.pwd) 
    except Exception, e: 
     print e 
     return 

    try: 
     self.cur = cnxn.execute(self.sql) 
    except Exception, e: 
     print e 
     return 

    self.wait_for_query(callback=self.async_callback(self.on_finish)) 

def wait_for_query(self, callback): 
    while 1: 
     data = self.cur.fetchone() 
     if not data: break 
     self.write(data) 
     self.flush() 
    callback() 

def on_finish(self): 
    self.finish() 

我看到这篇文章: Asynchronous COMET query with Tornado and Prototype ,知道我的解决办法是行不通的。但我当然不能使用add_timeout,因为我无法弄清楚迭代持续的时间。那么,我如何通过这个工作来实现我的目标呢?

回答

0

为了让单线程Tornado服务器在这样的请求中是异步的,您必须将控制权交还给I/O循环。试试这个:

class LongRequestHandler(tornado.web.RequestHandler): 
    def database_callback(self): 
     data = self.cur.fetchone() 
     if not data: 
      self.finish() 
      self.cnxn.close() 
     else: 
      self.write(data) 
      self.flush() 
      tornado.ioloop.IOLoop.instance().add_callback(self.database_callback) 

    @tornado.web.asynchronous 
    def get(self): 
     try: 
      self.cnxn = pyodbc.connect(self.server, self.driver, self.table, self.uid, self.pwd) 
     except Exception, e: 
      print e 
      return 

     try: 
      self.cur = self.cnxn.execute(self.sql) 
     except Exception, e: 
      print e 
      return 

     tornado.ioloop.IOLoop.instance().add_callback(self.database_callback) 

但值得注意的是,每个数据库提供者都是不同的。我的理解是,在MySQL中,大部分时间/处理实际上将花费在execute()调用上,而不是循环遍历数据,因为MySQL会处理整个查询并返回完整的结果集。如果您使用的是同样的数据库提供程序,则可能需要在位于Tornado后面的工作进程中处理这样的请求。

编辑我的例子只是一个例子。实际上,在返回之前,你需要测试你的回调函数,并且可能循环了好几行,否则你会浪费IO循环中函数间很多CPU时间切换,而不是实际处理请求。在做了一些测试后,我担心的是MySQL是真实的 - 执行/查询语句本身就是导致锁定的语句,所以这种解决方案在这种情况下确实无济于事。