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

cod*_*ing 11 java spring tomcat servlets spring-mvc

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

假设客户端正在对网页执行http POST,并且帖子包含分配表单元素.

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

request.getParamater("abc")
Run Code Online (Sandbox Code Playgroud)

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

Tom*_*icz 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")
    }

}
Run Code Online (Sandbox Code Playgroud)

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

$ nc localhost 8080
Run Code Online (Sandbox Code Playgroud)

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

POST /upload?name=foo HTTP/1.1
Host: localhost:8080
Content-Length: 10000000
Run Code Online (Sandbox Code Playgroud)

服务器端仍然没有任何反应.Tomcat接受了连接但尚未调用UploadServlet.doPost.但是当我点击Enter两次时,servlet会打印name参数但会阻塞getLines()(getInputStream()下面).

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

Servlets摘要

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

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

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

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]) {
        //...
    }

}
Run Code Online (Sandbox Code Playgroud)

收到HTTP标头后,Entering /upload/good将立即调用goodUpload处理程序方法,但如果您尝试读取body InputStream尚未收到正文,则会阻止它.

但是/upload/bad会等到整个POST身体可用,因为我们已经明确要求周身字节数组(String也有同样的效果)@RequestBody body: Array[Byte].

因此,Spring MVC如何处理大型请求主体取决于您.

TCP/IP级别

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

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