Большой JSON без сжатия это лишние мегабайты по сети. На быстром канале незаметно, а пользователь на мобильном интернете ждёт лишние секунды. Лечится одним middleware, который сжимает ответ перед отправкой.
Как это работает
GZIP хорошо жмёт текст с повторяющимися структурами, а JSON именно такой. Клиент говорит, что умеет принимать сжатое, через заголовок
Accept-Encoding: gzip. Сервер сжимает тело, ставит Content-Encoding: gzip и отдаёт. Клиент распаковывает сам, его код менять не нужно.Middleware
Проверяем заголовок клиента, оборачиваем
ResponseWriter в gzip.Writer и пропускаем через него ответ хендлера:func gzipMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
gzr := gzipResponseWriter{Writer: gz, ResponseWriter: w}
next.ServeHTTP(gzr, r)
})
}
type gzipResponseWriter struct {
http.ResponseWriter
Writer *gzip.Writer
}
func (g gzipResponseWriter) Write(b []byte) (int, error) {
return g.Writer.Write(b)
}Подключаем к нужному эндпоинту:
mux.Handle("/data", gzipMiddleware(http.HandlerFunc(dataHandler)))Проверяем размер ответа:
curl http://localhost:8080/data --output raw.json
curl -H "Accept-Encoding: gzip" http://localhost:8080/data --output gz.json
ls -lh raw.json gz.json
Когда не стоит
Уже сжатые форматы вроде JPEG, PNG или ZIP жать заново смысла нет, размер почти не упадёт, а CPU потратите. Совсем мелкие ответы тоже лучше не трогать, накладные расходы перевесят.
GZIP в Go это одно middleware и пара заголовков. Клиент менять не надо, а текстовый ответ ужимается в разы. В продакшене удобно взять готовый
gziphandler или включить сжатие на reverse proxy, но понимать, как оно устроено, полезно в любом случае.📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoDeep