【问题标题】:asynchronous call with Tornado and pyodbcTornado 和 pyodbc 的异步调用
【发布时间】:2011-03-03 16:27:31
【问题描述】:

我想实现一个基于tornado 的web 服务,它可以为用户提供数据库查询服务。我使用 pyodbc 模块连接到数据库并进行查询。在实践中,我发现打印查询结果需要很长时间。也就是说,如果我用下面的代码打印查询结果

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

sql 命令类似于

select * from <a large dummy table>

在循环结束之前,tornado 不会打印查询结果。而且需要很长时间。

我想利用tornado的非阻塞异步功能,让其他用户也可以使用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,因为我无法计算出迭代会持续多长时间。那么我怎样才能通过这个来实现我的目标呢?

【问题讨论】:

    标签: python tornado pyodbc


    【解决方案1】:

    为了让单线程 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 后面的工作进程中处理此类请求。

    编辑我的例子只是一个例子。实际上,您可能希望在返回之前测试您的回调,并可能遍历相当多的行,否则您会浪费大量 CPU 时间在 IO 循环上的函数之间切换,而不是实际处理请求。在做了一些测试之后,我担心 MySQL 是真的——执行/查询语句本身就是导致锁定的原因,所以这个解决方案在这种情况下真的无济于事。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-04
      • 1970-01-01
      • 2013-02-15
      • 1970-01-01
      • 2011-01-20
      相关资源
      最近更新 更多