我正在计划使用带有Twisted,Storm和Jinja的Python的讨论软件。问题是Jinja不是用于Twisted或异步套接字库,而使用Twisted提供的性能是我不打算使用Flask的原因。
那么,我怎样才能使用Jinja使用Twisted渲染网页?
答案 0 :(得分:15)
您可以使用Jinja呈现网页,就像在Twisted中使用任何其他Python库一样。你只需要打电话给它。这对Twisted来说可以正常工作,但如果Jinja做了一些阻塞,你可能会遇到性能问题。请注意,可能使用带有Twisted的阻塞库,只需通过deferToThread
,或者只是阻止主循环,如果它不是性能问题。所以,我推断你的问题实际上是关于如何在不阻挡的情况下使用Jinja。
Jinja是一个模板库,这意味着它会读取模板,在模板上调用一些视图逻辑,并编写一些HTML输出。所以有三件事可以阻止:
我不知道Jinja,所以我不确切知道这些东西的API是如何构建的,我不知道该怎么做,但我的猜测是那部分很容易;所以,我将给你一个关于第三方模板库和Twisted的一般答案。
所以我会解决这些问题中的每一个,尽管不是很合适:
这里最合理的做法是不关心它。阅读模板可能非常快。这些是经常访问的小文件,您的操作系统几乎肯定会保留在其文件系统缓存中。除非你把它们放在NFS上,否则你不太可能会阻止阅读它们。如果您对应用程序进行概要分析并发现这是一个问题 - 因为,比方说,您的磁盘速度非常慢或远程文件系统 - 只需在启动时将模板读入cStringIO
或类似内容并将其提供给jinja之后。
网页并不是那么大,而且Twisted没有提供阻塞API来写入套接字。相反,它提供了一个API,它只是将整个结果缓冲到内存中,直到可以写出来。我的建议是在这里做与读取模板基本相同的事情:除非你有非常大的输出,否则在响应被送到客户端时可能会烧掉一点RAM。
这是您最容易遇到问题的区域。 Jinja可能无法处理Deferred
的结果。但实际上,实际上并不是Jinja直接给你带来问题:它是Storm。当您访问某些属性时,Storm希望能够阻止,进行数据库查询。与数据库块交谈,这是大多数Web应用程序中阻止I / O的最有意义的来源。所以你需要决定如何处理这个问题。您有几个选择:
deferToThread
调用(或类似)中预先从风暴中获取所有内容,并确保Jinja仅访问内存中的对象。这样,您可以在主线程中运行渲染器,在执行数据库I / O的Deferred
的回调中运行。如果你只是访问内存中的对象,你的渲染器可能还需要一段时间,但这没关系。这个问题促使我post an article to my blog about the distinction between "blocking" and "running"作为草案搁置了很长一段时间;你可能想读它。如果这些选项都不适用于您,则从版本11.0开始Twisted has included a templating library that does support Deferred
s。您可以考虑使用twisted.web.template
替代Jinja。
答案 1 :(得分:2)
以下是如何实现解决方案3的示例示例,具有基本的延迟返回功能支持:
from jinja2 import Template
from twisted.internet import threads, reactor, defer
def inThread(f):
def new_f(*args, **kwargs):
return threads.deferToThread(f, *args, **kwargs)
return new_f
def fromThread(f):
def new_f(*args, **kwargs):
return threads.blockingCallFromThread(reactor, lambda: defer.maybeDeferred(f, *args, **kwargs))
return new_f
class DeferredTemplate(Template):
def render(self, **kw):
hooked_kw = {}
for k, v in kw.iteritems():
# decorate the callable so that they are run in the main thread
if callable(v):
v = fromThread(v)
hooked_kw[k] = v
return inThread(Template.render)(self, **hooked_kw)
from twisted.trial import unittest
class TestJinjaDeferred(unittest.TestCase):
@defer.inlineCallbacks
def test_basic(self):
def getHello():
d = defer.Deferred()
reactor.callLater(0.0, lambda: d.callback("Hello"))
return d
def getWorldSync():
return "world"
template = DeferredTemplate("{{ getHello() }} {{ getWorldSync() }}")
res = yield template.render(getHello=getHello, getWorldSync=getWorldSync)
self.assertEqual(u"Hello world", res)
答案 2 :(得分:0)
我认为Tornado模板系统(就像Jinja2模板一样,因为它类似于Django ......)可以在没有龙卷风的情况下使用:
我们试图清理代码库以减少模块之间的相互依赖性,因此您(理论上)应该能够在项目中独立使用任何模块,而无需使用整个软件包。