Pertanyaan Menggunakan generator python sederhana sebagai co-rutin dalam penangan async Tornado?


Saya memiliki fungsi generator python yang menghasilkan potongan teks. Saya ingin menulis get metode untuk tornado.web.RequestHandler subclass yang akan mengulang generator, menulis potongan ke respons saat berjalan.

Karena ini Tornado, dan karena generator dapat mengambil alih satu detik untuk diproses, saya pikir akan menyenangkan untuk membuat pawang asynchronous, menggunakan generator ini sebagai co-rutin dan melewati kontrol ke IOLoop setelah setiap bongkahan. Namun, saya tidak bisa membuat kepala atau ekor bagaimana melakukan ini.

Inilah contoh saya (memblokir) kode:

class TextHandler(web.RequestHandler):
    @web.asynchronous
    def get(self, n):
        generator = self.generate_text(100000)
        # Clearly, this will block. How to make it asynchronous?
        for text in generator:
            self.write(text)

    def generate_text(n):
        for x in xrange(n):
            if not x % 15:
                yield "FizzBuzz\n"
            elif not x % 5:
                yield "Buzz\n"
            elif not x % 3:
                yield "Fizz\n"
            else:
                yield "%s\n" % x

Bagaimana saya bisa membuat pawang ini bekerja secara asynchronous?


16
2018-01-11 00:45


asal


Jawaban:


Berikut ini versi dasar dari apa yang Anda gambarkan. Untuk menghindari pemblokiran, Anda dapat meneruskan generator Anda ke IOLoop melalui fungsi panggilan balik. Kuncinya di sini adalah karena Anda tidak menggunakan proses yang melakukan IO yang sebenarnya dan tidak memiliki proses level os / file handler untuk ditambahkan ke IOLoop melalui add_handler, Anda dapat menggunakan yang sederhana add_callback panggil dan panggil berulang kali dari dalam fungsi panggil balik untuk menjaga fungsi dalam antrian panggilan balik IOLoop hingga generator selesai.

import tornado.httpserver
import tornado.ioloop
import tornado.web

class TextHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.generator = self.generate_text(1000)
        tornado.ioloop.IOLoop.instance().add_callback(self.loop)

    def loop(self):
        try:
            text = self.generator.next()
            self.write(text)
            tornado.ioloop.IOLoop.instance().add_callback(self.loop)
        except StopIteration:
            self.finish()

    def generate_text(self, n):
        for x in xrange(n):
            if not x % 15:
                yield "FizzBuzz\n"
            elif not x % 5:
                yield "Buzz\n"
            elif not x % 3:
                yield "Fizz\n"
            else:
                yield "%s\n" % x

application = tornado.web.Application([
    (r"/text/", TextHandler),
])

http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()

16
2018-01-11 15:30



Juga dimungkinkan untuk menggunakan yang baru gen tornado antarmuka ke proses asinkron:

import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.gen

class TextHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):

        def cb(it, callback):
            try:
                value = it.next()
            except StopIteration:
                value = None
            callback(value)

        it = self.generate_text(1000)
        while True:
            response = yield tornado.gen.Task(cb, it)
            if response:
                self.write(response)
            else:
                break
        self.finish()

    def generate_text(self, n):
        for x in xrange(n):
            if not x % 15:
                yield "FizzBuzz\n"
            elif not x % 5:
                yield "Buzz\n"
            elif not x % 3:
                yield "Fizz\n"
            else:
                yield "%s\n" % x

application = tornado.web.Application([
    (r"/text/", TextHandler),
])

http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()

14
2018-01-11 18:19