티스토리 뷰

golang

gRPC Deadline

주먹불끈 2019. 6. 26. 15:16


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 이다.

 

 

 

반응형
반응형
잡학툰 뱃지
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
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
글 보관함