Ручная промывка golang http.ResponseWriter
Первоначально опубликовано на blog.simon-frey.eu (подсветка синтаксиса кода там лучше: D)
В недавнем проекте я хотел начать рендеринг HTML-страницы, даже если сервер все еще работал над длительной задачей. (Для отображения экрана загрузки https://unshort.link без использования JavaScript)
Мой код выглядел примерно так:
func h(rw http.ResponseWriter, req *http.Request) { io.Copy(rw,loadingHTMLByteReader)
tResult = performLongRunningTask()
rw.Write(tResult)
}
loadingHTMLByteReader
- это HTML-код, который я уже хотел отобразить, даже если сервер еще не может записать все результаты.
Но, к сожалению, это не дало ожидаемого результата. Страница по-прежнему была полностью отрисована сразу после завершения полного процесса.
Очевидно, go буферизует там писатель ответов до тех пор, пока обработчик не вернется или буфер (по умолчанию 4 КБ) не будет заполнен.
Буфер определяется через http.Transport
:
type Transport struct {
...
// WriteBufferSize specifies the size of the write buffer used
// when writing to the transport.
// If zero, a default (currently 4KB) is used.
WriteBufferSize int
...
}
Итак, есть два варианта достижения желаемого результата:
- установите WriteBufferSize на что-то очень маленькое
- ручная очистка буфера http.ResponseWriter
Вариант 1, очевидно, является неудовлетворительным решением, поскольку я не смог бы точно контролировать, что полный loadingHTMLByteReader
будет отправлен, если он перекрывает границы буфера.
Например. loadingHTMLByteReader
имеет размер 3 КБ, если размер буфера установлен на 2 КБ, последние 2 КБ не будут записаны до тех пор, пока буфер не будет заполнен дополнительными данными или не будет возвращен обработчик. Итак, я выбрал вариант 2
Как сбросить http.ResponseWriter
Изучая сеть, я нашел следующее решение, как вручную промыть http.ResponseWriter
:
if f, ok := rw.(http.Flusher); ok {
f.Flush()
}
Моя проблема решена и сайт работает как задумано:
func h(rw http.ResponseWriter, req *http.Request) { io.Copy(rw,loadingHTMLByteReader)
if f, ok := rw.(http.Flusher); ok {
f.Flush()
}
tResult = performLongRunningTask() rw.Write(tResult)
}
Важные заметки
Http.ResponseWriter не всегда реализует интерфейс Flusher.
Как указано в godoc, ResponseWriter не всегда реализует интерфейс Flusher, особенно когда вы используете оболочки для ResponseWriter.
Важно всегда проверять, работает ли утверждение типа, и только затем использовать Flusher для предотвращения паники сервера в крайние случаи
В сети есть другая буферизация
Очистка модуля записи ответов контролирует только буферизацию уровня приложения. У вас нет контроля над тем, что делают операционная система, сеть и клиент. Все эти объекты могут сами вводить дополнительное кэширование.
Ручную промывку следует использовать только для повышения производительности и наличия полезных функций. Не думайте, что вы можете использовать его для какого-либо критического поведения процесса, это только хорошо, чтобы иметь.
Источники:
https://stackoverflow.com/questions/19292113/not-buffered-http-responsewritter-in-golang
Первоначально опубликовано на https://blog.simon-frey.eu 15 января 2020 г.