当servlet处理请求时,是整个请求标头/正文/等。装了吗?

时间:2012-01-04 20:06:20

标签: java spring tomcat servlets spring-mvc

当向网页发出请求并且通过servlet(通过tomcat处理)处理它时,一旦在servlet级别(或spring mvc控制器)进入处理,就有整个请求头/身体的/ etc。已经从客户端发送到服务器了吗?

假设客户端正在对网页执行http POST,并且该帖子包含所有表单元素。

所有这些数据都将通过tomcat和您正在执行的servlet,或者如果您实际上没有引用:

request.getParamater("abc")

那么你不会因为它不会被流式传输而产生额外的负载?

1 个答案:

答案 0 :(得分:14)

我找不到引用,但我相信servlet在整个头文件可用后开始处理(所有请求头后跟两个换行符)。这就是为什么您getInputStream()getReader()而不是getBody()返回Stringbyte[]

这样,servlet可以在客户端仍然发送请求时开始处理请求数据,从而允许servlet处理大量内存占用的数据。例如,上传servlet可以逐字节读取上传的文件并将其保存到磁盘,而无需同时在内存中提供完整的请求内容。

这是我用于测试的servlet(在Scala中,对不起):

@WebServlet(Array("/upload"))
class UploadServlet extends HttpServlet {

    @Override
    override def doPost(request: HttpServletRequest, response: HttpServletResponse) {
        println(request.getParameter("name"));
        val input = Source.fromInputStream(request.getInputStream)
        input.getLines() foreach println
        println("Done")
    }

}

现在我使用nc来模拟慢客户端:

$ nc localhost 8080

服务器端没有任何事情发生。我现在手动发送一些HTTP标头:

POST /upload?name=foo HTTP/1.1
Host: localhost:8080
Content-Length: 10000000

服务器端仍未发生任何事情。 Tomcat接受了连接但尚未调用UploadServlet.doPost。但是当我按下 Enter 两次时,servlet会打印name参数,但会阻塞getLines()(下面的getInputStream())。

我现在可以使用10000000发送文本行(Tomcat需要nc个字节)并在服务器端逐步打印(input.getLines()返回Iterator[String]阻塞,直到新线路可用。)

Servlets摘要

  1. Tomcat在开始处理请求之前等待整个 HTTP标头(将其传递给匹配的servlet)

  2. doPost()调用之前,请求正文不必完全可用。这很好,否则我们很快就会耗尽内存。

  3. 这同样适用于发送回复 - 我们可以逐步执行此操作。

  4. Spring MVC

    使用Spring MVC,你必须要小心。考虑以下两种方法(注意不同的参数类型):

    @Controller
    @RequestMapping(value = Array("/upload"))
    class UploadController  {
    
        @RequestMapping(value = Array("/good"), method = Array(POST))
        @ResponseStatus(HttpStatus.NO_CONTENT)
        def goodUpload(body: InputStream) {
            //...
        }
    
        @RequestMapping(value = Array("/bad"), method = Array(POST))
        @ResponseStatus(HttpStatus.NO_CONTENT)
        def badUpload(@RequestBody body: Array[Byte]) {
            //...
        }
    
    }
    

    一旦收到HTTP标头,输入/upload/good将调用goodUpload处理程序方法但如果您还没有正文,则会尝试阅读body InputStream接收。

    然而/upload/bad将等到整个POST正文可用,因为我们已明确请求整个正文作为字节数组(String具有相同的效果):{{1 }}

    因此,Spring MVC如何处理大型请求主体。

    TCP / IP级别

    请记住,HTTP在TCP / IP之上工作。仅仅因为您没有调用@RequestBody body: Array[Byte] / getInputStream()并不意味着服务器没有从客户端接收数据。实际上,操作系统管理网络套接字并保持接收未消耗的TCP / IP数据包。这意味着来自客户端的数据被推送到服务器,但操作系统必须缓冲该数据。

    也许更有经验的人可以回答在这种情况下发生的事情(对于这个网站来说不是真正的问题)。如果服务器没有读取传入数据,O / S可能会突然关闭套接字,或者如果缓冲区变大,它可能只是缓冲它并交换?另一种解决方案可能是停止确认客户端数据包,导致客户端减速/停止。真的取决于O / S,而不是HTTP / servlets。