mod_wsgi 输出缓冲区而非返回
现在我有一个 mod_wsgi 脚本,结构是这样的……
def application(environ, start_response):
status = '200 OK'
output = 'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)
return [output]
我在想有没有办法把这个改成用 yield
的方式,而不是用 return
。这样的话,我就可以在页面生成的过程中就发送内容,而不是等到全部完成后再发送,这样用户加载页面的速度会更快。
不过,每当我把输出改成一个列表并在 application() 中使用 yield 时,就会出现错误:
TypeError: sequence of string values expected, value of type list found
3 个回答
不要发送内容的长度,而是随着你计算的结果直接发送输出。其实,如果你不发送Content-Length这个头信息,你就不需要知道输出的大小。这样的话,你可以在还没计算出全部内容之前,先发送一部分响应。
def application(environ, start_response):
status = '200 OK'
output = 'Hello World!'
response_headers = [('Content-type', 'text/html')]
start_response(status, response_headers)
yield head()
yield part1()
yield part2()
yield part3()
yield "<!-- bye now! -->"
否则,你就无法从分块发送中获得好处,因为计算输出的过程可能是最慢的,而互联网协议本身会把输出分成块发送。
可惜的是,这种方法在某些情况下不适用,比如当part2()的计算决定你需要更改某个头信息(比如cookie)或者需要构建其他全局数据结构时——如果出现这种情况,你就必须在发送头信息之前计算出整个输出,这样的话不如直接使用return [output]
。
举个例子, http://aaron.oirt.rutgers.edu/myapp/docs/W1200_1200.config_template 需要为页面顶部显示的子部分链接构建一个全局数据结构——所以最后一个子部分必须在第一块输出发送给客户端之前渲染出来。
要注意,除非真的必要,否则最好避免使用'yield'。特别是当你要输出很多小字符串时,使用'yield'会很低效。这是因为WSGI的规定要求每次输出一个字符串后,必须刷新响应。对于Apache/mod_wsgi来说,刷新意味着每个字符串都要通过Apache的输出系统强制发送到网络上。即使不考虑Apache的输出过滤系统,频繁地将很多小字符串写入网络本身就是个坏主意。
这个问题也出现在应用程序返回一个字符串数组时,因为在数组中的每个字符串之间也必须进行刷新。这是因为这些字符串被当作可迭代对象处理,而不是列表。因此,对于已经准备好的字符串列表,最好将这些字符串合并成一个大字符串,然后返回一个只包含这个大字符串的列表。这样做还可以让WSGI实现自动生成响应的Content-Length,如果没有明确提供的话。
确保在将列表中的所有字符串合并成一个字符串时,结果是以列表的形式返回。如果没有这样做,而是直接返回字符串,那么这个字符串会被当作可迭代对象处理,每个字符都会被当作一个单独的字符串。这会导致每输出一个字符就进行一次刷新,这样的效果比不合并字符串还要糟糕。
def application(environ, start_response): status = '200 OK' output = 'Hello World!' response_headers = [('Content-type', 'text/plain'), ('Content-Length', str(len(output)))] start_response(status, response_headers) yield output
"不过,每当我把输出换成一个列表并在 application() 中使用 yield 时,它就会报错:"
其实,不要把整个列表都用 yield。可以一个一个地输出列表里的每个元素:
for part in mylist:
yield part
或者如果这个列表就是你想要的全部内容,那就直接这样做:
return mylist
因为列表本身已经是一个可以逐个输出的东西了。