在日常工作中,经常会遇到一些看似简单但实际上相当复杂的问题。一个关于HTTP分块传输的问题,这个问题在一开始似乎得到了妥善解决,但随后却突然失效了。通过定期分析网站的瀑布图,注意到响应并没有像预期的那样分块传输。虽然从瀑布图中识别这一点并不容易,但如果对网站的性能足够熟悉,应该能够注意到这一点。由于发送给客户端的第一个分块只是HTML的'head'标签,这几乎不需要任何处理,因此可以立即发送给客户端,并且它立即导致浏览器开始下载'head'标签中请求的资源。如果响应是分块的,在瀑布图中,应该看到资源在客户端甚至完成下载站点的HTML响应之前就开始被下载。
一个正确的分块响应应该看起来像这样:如果仔细观察,会意识到响应下载需要很长时间,这与为这个测试选择的互联网连接不符,这意味着下载实际上并没有那么长,但服务器发送了部分响应,处理了更多内容,然后发送了剩余部分。
这里有一个没有分块的响应的图片:可以看到,客户端在完整页面下载完成后才开始下载'head'中所需的资源。本可以在这里节省一些宝贵的时间,并让服务器与从CDN下载资源的客户端并行工作。
发生了什么?就像说的那样,这曾经有效,但现在不再有效。回顾了最近完成的工作,并意识到最近更换了负载均衡器。由于没有正确地发送分块,新的负载均衡器不知道如何处理这个,因此只是将请求传递给客户端,而没有分块。
为了正确调查这个问题,开始直接与IIS服务器合作...发生了什么?使用Fiddler和WireShark查看了响应,并意识到响应是以分块的形式来的,但不是“正确”的。这意味着'Transfer-Encoding'头部没有设置,分块没有以正确的格式接收。响应只是被流式传输,有的每一部分都被传递给了客户端。在更换负载均衡器之前,它就是这样传递给客户端的,幸运的是,大多数客户端都能优雅地处理这个问题。
为什么分块没有正确格式化?当使用asp.net、mvc和IIS 7.5时,不必担心分块的格式。所要做的就是调用'HttpContext.Response.Flush()',响应应该为正确格式化。出于某种原因,这并没有发生...
由于没有使用经典的Microsoft MVC框架,而是在这里自定义构建了一些东西,开始深入研究框架。意识到这与框架无关,更多的是在Microsoft的Web程序集中的低层次,所以开始更深入地研究Microsoft的代码。
使用dotPeek,查看了'Response.Flush()'的代码...这就是看到的:如所见,IIS 6工作进程的代码被暴露出来,但当使用IIS7及以上版本时,它会转到一些非托管的DLL,这就是停止深入研究的地方。
开始寻找可能干扰的其他头部,并开始在互联网上搜索帮助...没有找到任何有用的东西(这就是写这篇文章的原因...),所以深入研究了设置。
突然间,意识到IIS设置中'启用HTTP保持活动'设置被禁用了。这增加了'Connection: close'头部,这干扰了这一点。
阅读了整个HTTP 1.1规范关于'Transfer-Encoding'和'Connection'头部的内容,并没有提到两者之间的任何联系。不管有没有意义,IIS 7.5(猜IIS 7也是如此,尽管没有测试)似乎没有正确格式化分块,也没有添加'Transfer-Encoding'头部,如果没有将'Connection'头部设置为'keep-alive'。
Microsoft -不能在某个地方,在某些文档中,或者至少在运行时遇到这些冲突设置时作为错误消息或警告输出吗?
那么这意味着什么?'Connection'头部指示客户端它正在处理的连接类型。如果连接设置为'Close',它表示连接不是持久的,将在发送完毕后立即关闭。当指定'keep-alive'时,这意味着连接将保持打开,客户端可能需要关闭它。
在分块响应的情况下,应该通过发送一个大小为'0'的分块来指示最后一个分块,告诉客户端它已经结束,他们应该关闭连接。这应该经过适当的测试,以确保没有让连接挂起,只是在服务器上浪费宝贵的资源。
另外,如果没有指定连接类型,默认将是'Keep-Alive')。