Gzip을 사용하는 Golang HTTP 핸들러
24035 단어 beginnerscompressiongzipgo
소개
Golang은 표준 라이브러리 내에 데이터를 압축하는 데 사용할 수 있는 다양한 종류의 압축 기술을 가지고 있습니다. 데이터 크기를 줄이기 위해 압축이 필요합니다. 웹 서버에서도 압축 기술은 클라이언트와 서버 간의 통신 속도를 높이는 데 도움이 됩니다. Gzip은 Golang과 웹 모두에서 지원하는 압축 기술 중 하나입니다. 이 문서에서는 gzip 요청/응답을 압축 해제하는 golang HTTP 핸들러 생성과 gzip 압축 본문 페이로드를 송수신하는 HTTP 요청을 생성하는 방법을 다룹니다. 이 문서에서 다루는 모든 코드는 this repository에서 찾을 수 있습니다.
디렉토리 구조
$ tree .
.
├── LICENSE
├── Makefile
├── README.md
├── client
│ └── main.go
├── curl.sh
├── go.mod
├── server
│ └── main.go
└── util.go
2 directories, 8 files
서버/main.go
package main
import (
"example"
"log"
"net/http"
)
func main() {
log.Println("server listening at :8000")
http.ListenAndServe(":8000", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body := example.MustReadCompressedBody[example.Payload](r.Body)
body.Number++
example.MustWriteCompressedResponse(w, body)
}))
}
서버는
read a compressed body
, increment the payload content
, 그런 다음 respond with new body
가 될 단일 엔드포인트만 실행합니다.util.go
유효 탑재량
압축을 테스트하기 위해 가장 간단한 페이로드를 사용할 수 있습니다. 예를 들면 다음과 같습니다.
type Payload struct {
Number int `json:"number"`
}
압축된 바디 페이로드 읽기
func MustReadCompressedBody[T any](r io.Reader) *T {
gr, err := gzip.NewReader(r)
PanicIfErr(err)
defer gr.Close()
var t T
PanicIfErr(json.NewDecoder(gr).Decode(&t))
return &t
}
gzip으로 압축된 페이로드를 읽으려면 응답 또는 요청에서
gzip.Reader
를 생성해야 합니다. 그런 다음 gzip.Reader
인스턴스를 사용하여 평소와 같이 JSON을 디코딩합니다.압축 응답 작성
func MustWriteCompressedResponse(w http.ResponseWriter, body any) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Encoding", "gzip")
gw := gzip.NewWriter(w)
defer gw.Close()
PanicIfErr(json.NewEncoder(gw).Encode(body))
}
평소와 같이 고객에게
Content-Type
및 Content-Encoding
에 대해 알리는 것이 좋습니다. 그러나 golang의 경우 클라이언트에 Content-Encoding
에 대해 알리면 golanghttp.Request
이 페이로드를 자동으로 압축 해제하는 데 도움이 됩니다. reading the payload 과 마찬가지로 gzip.Writer
만 생성한 다음 콘텐츠를 정상적으로 인코딩하면 됩니다. 또한 EOF 오류를 방지하려면 gw.Close()
를 호출하는 것을 잊지 마십시오.서버 테스트
이제 서버가 요청과 응답을 완전히 압축 해제했습니다. 먼저 cURL 명령을 사용하여 테스트해 봅시다.
# run the server on another terminal
# using `go run ./server/` command
$ echo '{"number": 99}' | gzip | \
curl -iXPOST http://localhost:8000/ \
-H "Content-Type: application/json" \
-H "Content-Encoding: gzip" \
--compressed --data-binary @-
서버가
Payload.Number
를 증가시키므로 100
로 요청을 보내면 응답 번호가 99
가 될 것으로 예상할 수 있습니다. 예상 결과는 다음과 유사합니다.HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: application/json
Date: Thu, 26 May 2022 12:04:53 GMT
Content-Length: 39
{"number":100}
이제 서버가 완전히 제대로 작동하는지 확인한 후 golang
http.Request
을 사용하여 HTTP 요청을 전송해 보겠습니다.클라이언트/main.go
package main
import (
"context"
"encoding/json"
"example"
"log"
"net/http"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
body := &example.Payload{
Number: 100,
}
log.Printf("create compressed request with %#v", body)
req := example.MustCreateCompressedRequest(ctx, http.MethodPost, "http://localhost:8000/", body)
defer req.Body.Close()
log.Printf("send compressed request")
resp, err := http.DefaultClient.Do(req)
example.PanicIfErr(err)
defer resp.Body.Close()
log.Println("resp.Uncompressed?", resp.Uncompressed)
var responsePayload *example.Payload
if resp.Uncompressed {
err = json.NewDecoder(resp.Body).Decode(&responsePayload)
} else {
responsePayload = example.MustReadCompressedBody[example.Payload](resp.Body)
}
log.Printf("read response %#v", responsePayload)
}
일반 HTTP 요청과 마찬가지로
create an HTTP.Request
, send the request
및 마지막으로 decode the response
만 있으면 됩니다. 그러나 요청 페이로드를 보내기 전에 먼저 압축하는 것을 잊지 마십시오.압축된 요청 만들기
func MustCreateCompressedRequest(ctx context.Context, method, url string, body any) *http.Request {
pr, pw := io.Pipe()
go func() {
gw := gzip.NewWriter(pw)
err := json.NewEncoder(gw).Encode(body)
defer PanicIfErr(gw.Close())
defer pw.CloseWithError(err)
}()
r, err := http.NewRequestWithContext(ctx, method, url, pr)
PanicIfErr(err)
r.Header.Set("Content-Type", "application/json")
r.Header.Set("Content-Encoding", "gzip")
return r
}
새로운 압축 요청을 생성하는 것은 write a compressed response과 같이 매우 쉽습니다. 새
gzip.Writer
를 생성해야 합니다. 위의 코드는 io.Pipe
를 활용하여 본문을 메모리에 버퍼링하고 불필요한 할당을 방지합니다. 그러나 본문을 먼저 버퍼링하려는 경우 대안은 다음과 같습니다.func MustCreateCompressedRequest(ctx context.Context, method, url string, body any) *http.Request {
var b bytes.Buffer
gw := gzip.NewWriter(&b)
err := json.NewEncoder(gw).Encode(body)
defer PanicIfErr(gw.Close())
r, err := http.NewRequestWithContext(ctx, method, url, &b)
PanicIfErr(err)
r.Header.Set("Content-Type", "application/json")
r.Header.Set("Content-Encoding", "gzip")
return r
}
여기서도
gw.Close()
가 필요합니다. gzip.Writer
를 닫지 않으면 다음과 유사한 EOF 오류가 표시됩니다.2022/05/26 19:23:58 http: panic serving [::1]:56436: unexpected EOF
마지막으로 압축 요청을 생성한 후 일반 요청처럼 요청을 전송하기만 하면 됩니다. 하지만 여기 까다로운 부분이 있습니다.
var responsePayload *example.Payload
if resp.Uncompressed {
err = json.NewDecoder(resp.Body).Decode(&responsePayload)
} else {
responsePayload = example.MustReadCompressedBody[example.Payload](resp.Body)
}
압축을 풀고 디코딩하려는 응답을 받은 후 resp.Uncompressed 에 유의하십시오. 서버가
Content-Encoding: gzip
헤더를 반환하면 here Golang이 decompress
페이로드를 시도하므로 example.MustReadCompressedBody[example.Payload](resp.Body)
를 사용할 필요가 없습니다. 그러나 요청에 r.Header.Set("Accept-Encoding", "gzip")
를 추가하면 자동으로 압축이 풀리지 않습니다.클라이언트 테스트
# run the server on another terminal
# using `go run ./server/` command
$ go run ./client
2022/05/26 19:41:34 create compressed request with &example.Payload{Number:100}
2022/05/26 19:41:34 send compressed request
2022/05/26 19:41:34 resp.Uncompressed? true
2022/05/26 19:41:34 read response &example.Payload{Number:101}
결론
Golang HTTP 처리기 및 요청에서 gzip 압축을 구현하면 코드가 약간 복잡해질 수 있지만 요즘 최신 브라우저/API 게이트웨이는 코드를 변경하지 않고도 이를 쉽게 구현할 수 있다고 생각합니다. 또한 NY Times에서 만든 이middleware는 이 압축을 구현하는 데 드는 노력을 최소화하는 데 도움이 될 수 있습니다. 이 문서here에서 사용된 모든 코드를 찾을 수 있습니다.
읽어 주셔서 감사합니다!
Reference
이 문제에 관하여(Gzip을 사용하는 Golang HTTP 핸들러), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/clavinjune/golang-http-handler-with-gzip-45ed텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)