python - 如何在从 Tornado 流式传输视频时减少阻塞

标签 python tornado

我想知道如何减少我的 Tornado 视频流代码块,这样我就不会影响我的网络应用程序中的其他处理程序。

我有一些遗留代码,我在 Tornado 中使用它们来流式传输传入的 jpeg 并将它们显示为 html 页面上的视频。我的处理程序看起来像这样。它已经运行了几年,但最近我注意到它实际上是一个巨大的资源消耗者。每当有东西调用此处理程序时,所有其他请求都会急剧减慢。

class VideoAPI(tornado.web.RequestHandler):
    '''
    Serves a MJPEG of the images posted from the vehicle.
    '''

    @tornado.gen.coroutine
    def get(self):
        host = self.get_argument("host")
        port = int(self.get_argument("port"))
        ioloop = tornado.ioloop.IOLoop.current()
        self.set_header("Content-type", "multipart/x-mixed-replace;boundary=--frame")
        self.served_image_timestamp = time.time()
        my_boundary = "--frame"
        for frame in live_video_stream(host,port=port):
            interval = .1
            if self.served_image_timestamp + interval < time.time():
                img = cv2.imencode('.jpg', frame)[1].tostring()
                self.write(my_boundary)
                self.write("Content-type: image/jpeg\r\n")
                self.write("Content-length: %s\r\n\r\n" % len(img))
                # Serve the image
                self.write(img)
                self.served_image_timestamp = time.time()
                yield tornado.gen.Task(self.flush)
            else:
                yield tornado.gen.Task(ioloop.add_timeout, ioloop.time() + interval)

在我的应用程序的其他地方,我有一些计算成本较高的代码,我可以在不占用太多 CPU 的情况下运行它们。它遵循以下模式:

class Example(tornado.web.RequestHandler):

    executor = ThreadPoolExecutor(3)

    @tornado.concurrent.run_on_executor
    def do_some_stuff(self):
        """Do something that's not asyncio-friendly, and computationally slow"""
        return something

    @tornado.gen.coroutine
    def get(self):
        result = yield self.do_some_stuff()
        self.write(result)

假设第二个示例是流视频最有效的模式,我如何将视频处理程序转换为看起来更像这样?我从其他人那里继承了视频处理程序代码,但我真的不知道如何复制循环部分。例如,我应该将循环留在执行器上运行的函数中吗?我应该将其保留在 @tornado.gen.coroutine 装饰方法中吗?我应该做一些完全不同的事情吗?

最佳答案

您需要识别代码中昂贵的部分并将它们移至其他线程;将其余代码留在协程中(这是进行像 self.write 这样的 Tornado 调用所必需的)。例如,我的猜测是主要问题在于线路

img = cv2.imencode('.jpg', frame)[1].tostring()

要将其移至另一个线程(使用比 run_on_executor 装饰器更现代的 Tornado 习惯用法),请将其放入函数中并按如下方式运行:

def expensive_fn():
    return cv2.imencode('.jpg', frame)[1].tostring()

img = yield ioloop.run_in_executor(None, expensive_fn)

如果问题是for frame in live_video_stream(host,port=port)那么您需要进行更广泛的重构以摆脱生成器模式,以便可以在单独的线程中对其进行迭代。

关于python - 如何在从 Tornado 流式传输视频时减少阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60716059/

相关文章:

python - Raspberry Pi 3B 和 RFID RC522 - Python TypeError

sockets - 关闭套接字时是否需要从epoll/kqueue中注销fd?

python - 处理 Tornado 请求处理程序中错误的更好方法

java - 如何在python中返回包含sqlalchemy中的对象的列表,类似于java中的List<OrderInfo>

python - scipy kmeans -- 如何获取集群之间的实际阈值

python - django 调试工具栏 - SuspiciousFileOperation : Attempted access to '/static/_img/A/B/image01.png' denied

python - OpenCV 2.1 Python 绑定(bind)段错误

angularjs - 在 Angular/tornado 网站中发布请求

python - Tornado:在运行所有测试之前重置数据库

python - 弹性 beantalk 上的 psycopg2 - 无法部署应用程序