扭曲多个并发或异步流
我正在用Python写一个应用程序,使用twisted.web框架来通过HTML5播放视频。
视频是通过static.File('pathtovideo').render_GET()
来提供的。问题是一次只能播放一个视频,因为这会占用整个进程。
有没有办法让视频播放变成异步的,或者说不阻塞进程,这里用哪个词都可以。
我试过使用deferToThread,但这仍然占用了进程。
这是我目前使用的类,其中Movie是一个ORM表,mid只是一个任意行的ID。
class MovieStream(Resource):
isLeaf=True
def __init__(self, mid):
Resource.__init__(self)
self.mid = mid
def render_GET(self, request):
movie = Movie.get(Movie.id == self.mid)
if movie:
defered = deferToThread(self._start_stream, path=movie.source), request=request)
defered.addCallback(self._finish_stream, request)
return NOT_DONE_YET
else:
return NoResource()
`
def _start_stream(self, path, request):
stream = File(path)
return stream.render_GET(request)
def _finish_stream(self, ret, request):
request.finish()
1 个回答
这段代码中看起来像是阻塞的部分其实是 Movie.get
这个调用。
用 deferToThread
来调用 _start_stream
是不对的,因为 _start_stream
使用了 Twisted 的 API(比如 File
以及 File.render_GET
使用的东西),在反应器线程之外使用 Twisted 的 API 是不合法的(换句话说,就是在用 deferToThread
调用的函数里不能使用这些 API)。
幸运的是,你只需要删除 deferToThread
的使用就能修复这个错误。
要解决 Movie.get
阻塞的问题,你需要找到一种异步访问数据库的方法。也许可以使用 deferToThread(Movie.get, Movie.id == self.mid)
,前提是实现 Movie.get
的数据库库是线程安全的。
顺便提一下,你也可以通过将数据库查找逻辑提前到资源遍历的层级更高的位置来避免 render_GET
的一些麻烦。
举个例子,我想你的 URL 可能像这样 /foo/bar/<movie id>
。在这种情况下,/foo/bar
这个资源会被请求 <movie id>
的子资源。如果你这样实现查找:
from twisted.web.resource import Resource
from twisted.web.util import DeferredResource
class MovieContainer(Resource):
def getChild(self, movieIdentifier):
condition = (Movie.id == movieIdentifier)
getting = deferToThread(Movie.get, condition)
return DeferredResource(getting)
(假设 Movie.get
是线程安全的)那么你基本上就完成了。
资源遍历将以 DeferredResource(getting)
构造的对象结束,当这个对象被渲染时,它会处理等待 getting
有结果(在行话中就是让 Deferred "触发")并调用正确的方法,比如 render_GET
,来为请求生成响应。