#playframework #akka-stream #akka-http
#playframework #akka-stream #akka-http
Вопрос:
У меня вопрос о BodyParser.Raw
в игре! фреймворк. В официальном документе говорится:
Анализирует тело как
RawBuffer
. При этом будет предпринята попытка сохранить тело в памяти, вплоть до размера буфера памяти, настроенного Play, но в случае превышения этого размера он будет записан в файл.
Мне трудно понять приведенное выше описание. Означает ли это следующее —
Если я добавлю в свой application.conf
файл следующие конфигурации:
play.http.parser.maxMemoryBuffer=128k
play.http.parser.maxDiskBuffer=1G
и скажите, что тело моего запроса имеет размер 15 МБ, затем играйте! будет считывать / анализировать первые 128 КБ из тела запроса, записывать его в файл, а затем передавать / анализировать следующие 128 КБ из тела запроса, записывать его в тот же файл, …, пока тело запроса размером 15 МБ не будет полностью проанализировано? Это означает, что мы используем только 128 КБ памяти и можем обрабатывать тело запроса размером 15 МБ??
Я знаю, это звучит слишком хорошо, чтобы быть правдой…
Не мог бы кто-нибудь любезно объяснить это?
Обновить:
Я хочу добавить некоторые обновления здесь, если кто-то столкнулся с такой же проблемой здесь. Итак, @Ivan Kurchenko прав (см. Ниже Его Ответ): BodyParser.Raw
может использоваться для POST
тела запроса, которое больше настроенного play.http.parser.maxMemoryBuffer
. Играйте! в этом случае запрос будет буферизован с использованием диска. Другими словами, вы можете POST
создать большое тело запроса, которое даже больше, чем доступная память в вашей JVM.
Причина, по которой мой тест не прошел с использованием Chrome (а также Firefox), заключалась в том, что у меня был включен CSRF-фильтр по умолчанию. Как только я отключил его, все работает нормально. Например, следующие конфигурации,
play.http.parser.maxMemoryBuffer=1k
play.http.parser.maxDiskBuffer=4G
play.filters.disabled =play.filters.csrf.CSRFFilter
говорит, что если тело запроса больше 1 кб, играйте! будет использовать ваш диск для буферизации вашего запроса (и, конечно же, я отключаю фильтр CSRF). Теперь, с этой конфигурацией, я могу успешно загрузить фильм размером более 3G. Действительно, BodyParser.Raw
может использоваться, если вам нужно обработать большой запрос. HTH!
Ответ №1:
Не совсем — если размер запроса превышает пороговое значение памяти, в вашем случае, настроенное как play.http.parser.maxMemoryBuffer=128k
целое тело запроса, будет записано во временный файл или иным образом записано в память. И затем для следующего синтаксического анализа тело также будет передаваться либо из файла, либо из памяти. Давайте посмотрим вместе:
bodyParser.raw — создает RawBuffer
внутри, как, например, здесь https://github.com/playframework/playframework/blob/6d0789468909d5d7bdabc6c4207337bd7a7ca9b1/core/play/src/main/scala/play/api/mvc/BodyParsers.scala#L612
И внутри RawBuffer
двух методов, которые нас интересуют: реализация метода push, который принимает входящие байты, а общий размер превышает размер буфера, выполняется backToTemporaryFile
, что означает запись всех запросов во временный файл.
@volatile private var inMemory: ByteString = initialData
@volatile private var backedByTemporaryFile: TemporaryFile = _
@volatile private var outStream: OutputStream = _
private[play] def push(chunk: ByteString): Unit = {
if (inMemory != null) { //checks whether current in memory buffer exists
//if next readed chunk of request body exceeds in memory buffer size
if (chunk.length inMemory.size > memoryThreshold) {
backToTemporaryFile() // create temporary file
outStream.write(chunk.toArray)// write buffer to temporary file
} else {
inMemory = inMemory chunk // append in memory buffer with next chunk
}
} else {
outStream.write(chunk.toArray) // append in memory buffer with next chunk
}
}
А затем реализация метода asBytes, которая на своей стороне считывает байты, если в буфере памяти существует. Или реализация asFile, которая считывает байты из временного файла.
Также обратите внимание: если размер тела входящего запроса превышает заданное значение, play.http.parser.maxDiskBuffer
Play ответит статусом 413 — REQUEST_ENTITY_TOO_LARGE.
Комментарии:
1. Привет @Ivan, спасибо за объяснение! Методы чтения, на которые вы указали, действительно помогают. Еще несколько вещей: во-первых, настроенный порог памяти в моем случае
play...maxMemoryBuffer=128k
—play...maxDiskBuffer=1G
это размер буфера жесткого диска … так что, я думаю, это была опечатка в вашем ответе? Во-вторых, я написал крошечное приложение, чтобы протестировать это, для имитации большого тела запроса я загружал большой файл. Теперь, если `play …. maxMemoryBuffer’ настроен на < размер файла, Chrome сразу выдает «Этот сайт недоступен», похоже, логика, которую вы описали в своем ответе, никогда не срабатывала??2. @lee Большое вам спасибо за внимание к деталям, да, вы правы, это была опечатка, а также я добавил фрагмент кода с некоторыми комментариями.
3. @lee Я не уверен насчет Chrome — для тестирования таких низкоуровневых вещей я предпочитаю использовать надлежащие инструменты, такие как Postman или curl, которые могут предоставить больше деталей низкого уровня HTTP.
4. @lee Я обновил ответ об ограничениях размера.
5. @lee я не уверен относительно rest Play! синтаксический анализатор — нужно посмотреть индивидуально. Но некоторые только в памяти — например, Json или String.