gRPC 공부 내용
gRPC 대신에 흔히 가장 많이 사용되는 것이 JSON을 사용한 HTTP API이다. HTTP API는
Client ←→ Server
Server ←→ Server
간 서로 데이터를 주고 받을 때 사용되는 통신 방법이라고 생각할 수 있다.
아래 예제에 Client가 HTTP로 RESTful하게 서버에게 GET으로 요청 한다.
그리고 서버는 요청에 대해 JSON으로 결과값을 전달하게 된다. (HTTP와 REST라는 개념은 거의 항상 붙어다녀서 사용된다,)
HTTP API는 데이터를 서로 주고 받을때 사용된다.
gRPC는 HTTP API 통신과 비슷하지만 차이점은 Proto Request를 전달하고 JSON 대신 Proto Response를 전달하는 것으로 생각한다.
큰 틀에서 봤을때 HTTP와 마찬가지로 데이터를 주고받는 방식 중 하나라고 볼 수 있다.
RPC(Remote Procedure Call)는 원래 분산환경 시스템에서 서로 다른 컴퓨터 프로그램들이 서로 다른 주소에서 서로를 호출하지만 마치 같은 주소에서 호출하는 것처럼 작동하게 하는 원격 프로시져 프로토콜이다.
즉, 프로그램들은 서로가 누구인지 알 필요 없이 정해진 방식대로 단순히 함수 호출만 하면 되는 것이다.
위 그림에서 Ruby Client는 통신하고 있는 C++ Service가 어느 위치에 있던, 어떤 코드로 작성되었던 간에 상관 없이 정해진 Proto라는 규격만 있으면 자유롭게 데이터를 주고 받을 수 있는 것이다.
그렇다면 gRPC는 무엇일까?
Google이 만든 protobuf(a.k.a protocol buffer)라는 방식을 사용해 RPC라는 프로토콜로 데이터를 주고 받는 플랫폼을 의미한다.
protocol buffer라는 방식은 XML과 같이 구조화된 데이터를 직렬화(serialize)하는 방식인데 압축을 해서 훨씬 빠르고, 사용법도 간단하고, 데이터의 크기도 작다.
JSON 직렬화보다 최대 8배 더 빠를 수 있다고 한다
gRPC로 통신하기 위해서는 protocol buffer로 어떻게 데이터를 주고 받을 것인지 정의해놔야 한다.
그렇기 때문에, 데이터를 주고 받을 때는 어떤 형식으로 전달해야하는지에 대한 가이드가 코드로 생성되기 때문에 따로 API 문서같은 것을 만들 필요가 없게 되는 것이다.
여러 회사에서도 이런 엄격한 사양 덕분에 개발 생산성이 높아지는 이유도 있어서 gRPC를 사용하는 경우가 다수 있다. ref. 뱅크샐러드 기술 블로그
RPC는 HTTP/2를 지원한다.
gRPC는 HTTP/1.1과 호환되지만 HTTP/2만의 고급 기능들도 사용할 수 있다.
동일한 연결로 병렬적인 요청을 처리할 수 있고, 연결을 유지해서 connection을 매번 하는데 사용되는 cost도 줄일 수 있다.
gRPC의 이러한 장점들은 요새 유행하고 있는 Microservice에 안성맞춤이다. Microservice는 간단히 말하면 서비스의 모든 기능들을 하나의 큰 서버에 몰아놓는 것이 아닌, 각각의 독립적인 기능으로 여러 서버로 만들고 서버들간 통신하게 만드는 아키텍처를 의미한다
생성된 파일은 각 클라이언트가 참조할 수 있는 언어(.java .c .go 등..) 로써 bean 과 같이 데이터를 엑세스 하거나 핸들링하는 함수가 포함되어 있습니다.
stub 을 활용해 실행될 프로시저를 구현하거나 전달할 파라미터를 생산할수 있습니다.
config/config.proto
syntax = "proto3";
package test;
service Test {
rpc GetSum(Data) returns (Sum) {}
}
message Data {
int32 a = 1;
int32 b = 2;
}
message Sum {
int32 sum = 1;
}
server.py
from concurrent import futures
import grpc
from python_out.service_pb2 import Data, Sum
import python_out.service_pb2_grpc as service_pb2_grpc
class TestServicer(service_pb2_grpc.TestServicer):
def __init__(self):
pass
def GetSum(self, request: Data, context):
print('a:{0} b:{1}'.format(request.a,request.b))
return Sum(sum=request.a + request.b)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
service_pb2_grpc.add_TestServicer_to_server(
TestServicer(), server
)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
run.py
from grpc_tools import protoc
protoc.main((
'',
'-Iprotos',
'--python_out=.',
'--grpc_python_out=.',
'protos/service.proto',
))
client.py
import grpc
import python_out.service_pb2 as service_pb2
import python_out.service_pb2_grpc as service_pb2_grpc
def get_sum(stub: service_pb2_grpc.TestStub):
response = stub.GetSum(service_pb2.Data(a=1, b=2))
print(f"Received sum: {response}")
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = service_pb2_grpc.TestStub(channel)
print("-------------- Get Sum --------------")
get_sum(stub)
if __name__ == '__main__':
run()
gRPC는 브라우저-서버 간의 통신이 지원되지 않는 단점이 존재하는데 이는 gRPC-Gateway를 이용하면 해결할 수 있다.
gRPC-Gateway는 프로토콜 버퍼 컴파일러(protoc)의 플러그인이다.
gRPC 서비스 정의(proto file)을 읽고 리버스 프록시 서버를 생성하는데 이 서버가 RESTful HTTP API를 gRPC로 변환해주는 역할을 한다.
아래 그림은 gRPC 클라이언트(Stub)과 리버스 프록시 서버를 생성한 뒤 RESTful HTTP API 요청을 gRPC로 변환하는 것이다.
참고
https://devjin-blog.com/golang-grpc-server-1/
https://docs.microsoft.com/ko-kr/aspnet/core/grpc/comparison?view=aspnetcore-6.0
좋아요처음으로