2

ユーザーにデータベースクエリサービスを提供できる 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 を使用することはできません。なぜなら、反復がどれくらい続くかを把握する方法がないからです。では、どうすればこれを乗り越えて目標を達成できるでしょうか?

4

1 に答える 1

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() 呼び出しに費やされることを私は理解しています。同じことを行うデータベース プロバイダーを使用している場合は、Tornado の背後にあるワーカー プロセスでこのような要求を処理する必要がある場合があります。

編集 私の例は単なる例です。実際には、コールバックをテストし、戻る前にかなりの数の行をループする可能性があります。そうしないと、実際にリクエストを処理するのではなく、IO ループで関数間を切り替えるために非常に多くの CPU 時間を無駄にします。MySQLに関して私が恐れていたことをいくつかテストした後、実行/クエリステートメント自体がロックの原因であるため、このソリューションはその状況では実際には役に立ちません。

于 2011-11-06T18:13:13.187 に答える