티스토리 뷰
Photo by Christian Joudrey on Unsplash
개요
gRPC 에서 사용하는 Deadline 을 이해하고 실습해보자
참고 Udemy 강좌
- https://www.udemy.com/grpc-golang/learn/lecture/11018816#overview
- https://www.udemy.com/grpc-golang/learn/lecture/11018818#overview
참고 링크: https://grpc.io/blog/deadlines/
최종구현 코드 링크: https://github.com/nicewook/grpc-deadline
시작해보자
TR; DR: 항상 Deadline 을 설정하는 것을 권장함.
gRPC 라이브러리는 통신, Marshalling/Unmarshlling, 그리고 데드라인을 챙겨준다.
gRPC 클라이언트는 데드라인을 설정할 수 있으며, 넘어섰을때에 DEADLINE_EXCEEDED 와 함께 종료될때까지 기다린다.
언어에 따라 정확한 값은 달라지지만 기본 데드라인은 매우 큰 값이다.
- 따라서 설정을 안해두면 기본값인 매우 긴 시간동안 메모리 등등의 리소스를 잡아먹을 가능성이 있고,
- 서비스가 지연되거나, 심지어는 서비스가 뻗어버릴 수도 있다.
데드라인으로 고정된 값으로 다루는 언어도 있고, timeout 을 쓰는 언어도 있다.
서비스 측에서는 기술적으로 가능한 가장 긴 데드라인을 지원하고
클라이언트 측에서는 필요한 한에서는 응답을 기다려주는게 좋다.
데드라인은 서비스마다 적절한 시간범위가 다를 수 밖에 없다.
대략적으로라도 계산을 해서 적정 데드라인을 잡아주는 수 밖에 없다.
gRPC 클라이언트와 서버는 RPC 가 성공했는지 여부를 각자 독립적으로 판단한다.
예를 들면, 서버는 성공적으로 시간안에 마쳐서 리턴했지만, 클라이언트가 받았을때는 이미 데드라인을 넘겨버려 실패!
→ 이런것 까지 gRPC 가 챙길 수는 없으니 상위 어플리케이션 단에서 챙겨줘야 한다.
gRPC Deadline chained.
무슨 말이냐면 A → B 에게 요청하고 이걸 B → C 로 요청할때에는 데드라인이 함께 넘겨진다는 것이다.
이미 데드라인이 넘어버렸는데 C 가 쓸데 없이 작업할 필요가 없으니깐.
코드예시를 보자
데드라인 설정하기
clientDeadline 은 expire 되는 정확한 시간을 담게 된다. (음, 그럼 서버랑 클라이언트 시간은 동기화 되어 있어야 겠네)
데드라인 체크하기
이건 클라이언트가 Cancel 했는지 여부를 확인해보는 코드이다.
데드라인 조정하기
Golang 에서의 Timeout 과 Deadline 이 같은 것이다. context.WithTimeout() 이 리턴하는 값이 context.WithDeadline() 이다.
아래 코드와 같이 데드라인을 파라미터로 받아서 실행할 수 있게 해둘 수 있다.
실습해보자
1) 들어오는 문자열에 대해 각 문자의 개수 정보를 리턴해주는 gRPC 를 구현해본다.
2) 서버는
- 100 ms 마다 Cancel 여부를 체크
- 1000 ms 있다가 리턴 하도록 구현하겠음
3) 클라이언트는
- 첫번째는 데드라인을 500 ms 으로 하고
- 두번째는 데드라인을 1500 ms 으로 해서 보내겠음
- 세번째는 데드라인을 500 ms 으로 하되 300 ms 쯤에 Cancel() 하도록 해보겠음
1) 전체 구조
|
server, client 폴더가 있고 - 각각은 server.go, client.go 파일을 가지고 있다 (각각은 package main)
proto 폴더에는 cntchar.proto 파일이 정의되어 있고, 컴파일하여 .pb.go 파일이 생성되어 있다.
gen.bat 에는 .proto 파일을 컴파일하는 명령을 저장해두었다. - 매번 변경한 다음 컴파일 하려면 이걸 그냥 쓰면 되겠지?
|
1) .proto 를 만들고, protoc 로 컴파일하기
|
1) golang 만의 package 명을 "cntcharpb" 라고 해두었다.
2) request/response message 를 정의 - 클라이언트는 입력 문자열을 보내게 되고 - 서버는 입력 문자열의 각 문자의 개수를 회신하게 된다.
3) service 는 cntCharReq 를 받아서 cntCharRes 를 리턴해주는 cntChar() rpc 함수를 정의해두었다. - stream 이 아닌 unary 이다. 하나의 request 에 대해 하나의 response 를 회신한다.
컴파일은 아래와 같이 하였다. protoc proto\cntchar.proto --go_out=plugins=grpc:. |
2) server.go 와 client.go 틀잡기
핵심 기능을 제외한 RPC 기능만을 구현하여 틀을 잡아보자.
server.go
1) 127.0.0.1:50051 tcp 를 listen 하는 녀석을 하나 만들고 2) gRPC server 도 하나 생성한다. 3) ChatChar 함수를 가지고 있는 CntCharServiceServer 인터페이스인 server type 을 정의한다. - 현재는 실행을 알리는 출력과 빈 리턴값만 있다. 4) 생성한 gRPC server 에 위에서 생성한 server type 을 Register 해준다. 5) 생성한 gRPC server 를 Serve 해준다.
실행은 go run server/server.go |
|
client.go
1) gRPC 로 Dial 하여 연결을 만든다. (cc) 2) 이 연결을 사용하는 Client 인 c 를 생성한다. 3) Client c 가 가지고 있는 함수인 CntChar() 를 이용하여 server RPC call 을 한다. 4) res 를 받아서 출력해준다.
실행은 go run client/clientr.go |
|
3) server.go 와 client.go Count 구현
server.go
실제 CntChar() 를 구현해본다.
1) 클라이언트에게서 온 request 의 StrInput 을 protoc 가 자동 생성해준 GetStrInput() 을 이용하여 추출해낸다. 2) map 을 이용해서 각각의 문자 개수를 계산한 다음 3) fmt.Sprintf 를 이용해서 수신한 문자열과, 문자열 속 문자들의 개수를 → 하나의 문자연 cntResult 에 담는다 4) cntcharpb.CntCharRest 에 담아 리턴해준다.
실행은 go run server/server.go |
|
client.go
Client 는 더욱 간단하다. 1) 아까는 빈 &cntcharpb.CntCharReq 를 보냈지만 2) 이번에는 값을 담아서 보내면 된다.
실행은 go run client/clientr.go |
|
서버와 클라이언트의 출력은 각각 아래와 같다.
4) 데드라인 구현
server.go
1) 고루틴 하나를 실행한다. - 1ms 마다 클라이언트로부터 Cancel 이 오는지를 체크한다. - Deadline exceeded 인 경우에서 서버로서는 클라이언트로부터의 Cancel 로 인식한다.
2) select 문을 통해 분기한다. - 300 ms 까지 클라이언트로부터 Cancel 이 오지 않으면 계산한 값을 리턴해준다.
실행은 go run server/server.go |
|
client.go
우선 클라이언트를 생성한 다음 이후의 작업은 함수로 호출하였다.
reqWithDeadline(c, 500*time.Millisecond, in) - 서버는 300 ms 이후에 결과를 회신주는데 Timeout 을 500ms 주었으므로 정상적으로 회신을 받을 것이다. reqWithDeadline(c, 100*time.Millisecond, in) - Timeout 을 100ms 주었기에 서버가 회신주기 전에 서버로 Cancel 을 날릴 것이다. reqWithDeadlineCancel(c, 500*time.Millisecond, in) - Timeout 은 넉넉히 주었지만 내부적 구현을 100ms 이후에 cancel() 을 날리도록 해두었다.
실행은 go run client/clientr.go
|
중간에 cancel 을 하지 않는 구현이다.
1) status.FromError() 는 - *Status 와 ok 를 리턴하는데 - ok는 gRPC package 에서 만들어진 것인지를 회신준다. 2) 따라서 gRPC 에서 생성된 err 의 경우를 별도로 처리해준다. 3) statusErr.Code() 를 통해 에러코드를 확인할 수 있으며, 여기서는 - codes.DeadlineExceeded 와 - codes.Canceled 를 체크하여 처리하였다.
|
이 함수는 100ms 이 되면 무조건 cancel() 을 실행하여 gRPC 연결을 종료해버린다.
|
실제 실행은 아래와 같다.
1) 정상동작
2) 정해둔 Timeout 을 넘어서서 Deadline exceeded
3) 정해둔 Timeout 내에 클라이언트가 cancel() 을 날림
참고
|
부모 데드라인보다 땡기려면 사용한다. 그런데 부모 데드라인이 더 빠르다면? - 부모 == 자식 데드라인
이 함수의 리턴값인 CancleFunc 는 말 그대로 함수인데 언제든 이게 호출되면 뜷렸던 채널이 닫히게 된다. (부모꺼든 자식꺼든 상관없음)
리소스를 풀어주기 위해서라도 언제든 작업이 완료되거나 필요없어지면 Cancel 해줄것
예제는 ctx.Done() 이 50ms 뒤에 불리면서 끝날꺼다. |
|
WithTimeout 이 리턴해주는게 WithDeadLine 이다. |
|
|
'golang' 카테고리의 다른 글
gRPC SSL/TLS 1. 암호에 대하여 - 대칭키, 비대칭키, 해시 알고리즘 (0) | 2019.07.01 |
---|---|
gRPC Error in Golang (0) | 2019.06.27 |
Golang 개발시 Makefile 사용해보기 (0) | 2019.06.24 |
Maximum Subarray Problem (0) | 2019.06.19 |
Concurrent Logging - in Golang (0) | 2019.06.17 |
- Total
- Today
- Yesterday
- API
- 제이펍
- Gin
- 영화
- golang
- agile
- 2023
- github
- 인텔리제이
- 잡학툰
- go
- solid
- Shortcut
- intellij
- pool
- notion
- Bug
- folklore
- 독서
- 노션
- strange
- OpenAI
- 체호프
- JIRA
- ChatGPT
- postgres
- bun
- 클린 애자일
- websocket
- 독서후기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |