Когда запрос обрабатывается сервлетом, это весь заголовок запроса / тело / и т.д. загружен уже? - PullRequest
11 голосов
/ 05 января 2012

Когда запрос делается на веб-странице и обрабатывается с помощью сервлета (который обрабатывается с помощью tomcat), после ввода обработки на уровне сервлета (или пружинного контроллера mvc), появляется полный заголовок запроса /тела / и т.д..отправлено с клиента на сервер уже?

Скажем, клиент выполняет http POST для веб-страницы, а запись содержит множество элементов формы.

Будут ли отправлены все эти данныечерез tomcat и ваш исполняющий сервлет или если вы на самом деле не ссылаетесь:

request.getParamater("abc")

Тогда вы не будете нести эту дополнительную нагрузку, поскольку она не будет передаваться в потоковом режиме?

1 Ответ

14 голосов
/ 05 января 2012

Я не могу найти ссылку, но я верю, что сервлет начинает обрабатывать, как только весь заголовок становится доступным (все заголовки запроса сопровождаются двумя символами новой строки).Вот почему у вас есть getInputStream() и getReader(), а не getBody(), возвращающие String или byte[].

Таким образом, сервлет может начать обрабатывать данные запроса, пока клиент все еще отправляет их, что позволяетсервлеты для обработки очень больших объемов данных с небольшим объемом памяти.Например, сервлет загрузки может прочитать побайтовый файл загруженного файла и сохранить его на диске без необходимости иметь полное содержимое запроса в памяти одновременно.

Вот сервлет, который я использовал для тестирования (в 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 , сервлет печатает параметр name, но блокирует getLines() (getInputStream() снизу).

Теперь я могу отправлять строки текста (Tomcat ожидает 10000000 байт), используя nc, и они постепенно распечатываются на стороне сервера (input.getLines() возвращает блокировку Iterator[String] до появления новой строки).

Сводка по сервлетам

  1. Tomcat ожидает HTTP-заголовок целом , прежде чем он начнет обрабатывать запрос (передавая его соответствующему сервлету)

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

}

Ввод /upload/good вызовет goodUpload метод обработчика, как только будет получен заголовок HTTP, но он заблокируется, если вы попытаетесь прочитать 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 / сервлетов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...