如何在mod_wsgi下控制werkzeug的HTTP分块使用

0 投票
1 回答
880 浏览
提问于 2025-04-18 00:10

在以下环境下:Apache -> mod_wsgi -> python -> werkzeug

我该如何控制是否使用HTTP分块传输,以及分块的大小呢?

后续问题:

我担心的是这两种情况的区别:

response = werkzeug.wrappers.Response()
response.response = very_long_string
return response

和这:

response = werkzeug.wrappers.Response()
response.response = [ very_long_string ]
return response

在第一种情况下,werkzeug会逐个字符地遍历字符串,并且每次发送一个字符。我不确定每个字符是否都是单独作为一个分块发送的,但我知道这种方式比第二种情况慢得多,因为在第二种情况下,整个字符串是一起发送的。

这两种情况的速度差异是因为分块传输吗?还是有其他原因?

1 个回答

1

一般来说,wsgi 规定一旦应用程序生成了响应内容的任何部分,就应该立即将数据发送给客户端,而不进行缓冲。也就是说,如果你这样做:

CONTENT = "a bit more content\n"

def my_slow_application(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    yield CONTENT
    sleep(1)

值得注意的是,如果没有 Content-Length 这个头信息,网关(在你的情况下是 apache/mod_wsgi)就无法判断它是否已经接收到所有的响应内容。而且因为不允许进行缓冲,它就必须使用分块传输编码。

另一方面,考虑一下:

def my_slowish_application(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain"),
                              ("Content-Length", str(len(CONTENT))])
    yield CONTENT
    sleep(1)

因为应用程序已经指定了内容长度,并且第一个 yield 的数据块正好是这个长度,所以网关知道不会再有更多的数据来了;它可以选择是否使用分块编码,完全由它自己决定。同样,

def my_fast_application(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    return [CONTENT] * 100

通常不会导致分块;响应是一个列表,大小是有限的,所以网关知道它已经收到了完整的响应。即使应用程序没有明确提供头信息,网关也可以通过 sum(map(len, app_iter)) 来计算内容长度,然后尽可能快地发送响应,只要网络允许。


至于如何控制“块”的大小,如果客户端连接被阻塞,网关可能会缓冲这些块;你的应用可能会

yield "foo"
sleep(1)
yield "bar"
sleep(1)
yield "baz"

但是如果在处理“foo”块时客户端连接被阻塞,网关可能会将 bar 和 baz 一起缓冲,当连接再次准备好读取时,将它们作为一个整体发送出去。简而言之,你无法控制分块的过程,你可能会强制它发生,也可以经常防止它发生(大多数网关在不需要时不会进行分块);但这并不适合用于框架。

撰写回答