[gRPC] Python gRPC Server/Client 구현
- gRPC
- 2023. 3. 4. 05:35
참조
소개
- 안녕하세요. 오늘은 Python 에서 gRPC Server/Client 코드 구현하는 방법에 대해서 설명 드리려고 합니다.
- 간단하게 calc.proto 버퍼 하나를 생성하고, Python 코드로 gRPC Server/Cleint 를 구현하여 통신하는 예제 코드를 작성하도록 하겠습니다.
calc.proto 버퍼 작성
- 제일 먼저, calc.proto 프로토콜 버퍼 하나를 생성하고 아래와 같이 내용을 작성합니다.
- 간단히 calc.proto 버퍼 파일 안에는 덧셈, 뺄셈, 곱셈, 나눗셈의 Input, Output 결과값을 Server, Client 에서 알 수 있는 Service와 Message 들을 작성하였습니다.
syntax = "proto3";
package calc;
service Calculator {
rpc Add (AddRequest) returns (AddReply) {}
rpc Substract (SubstractRequest) returns (SubstractReply) {}
rpc Multiply (MultiplyRequest) returns (MeuliplyReply) {}
rpc Divide (DivideRequest) returns (DivideReply) {}
}
message AddRequest {
int32 n1=1;
int32 n2=2;
}
message AddReply {
int32 n1=1;
}
message SubstractRequest {
int32 n1=1;
int32 n2=2;
}
message SubstractReply {
int32 n1=1;
}
message MultiplyRequest {
int32 n1=1;
int32 n2=2;
}
message MeuliplyReply {
int32 n1=1;
}
message DivideRequest {
int32 n1=1;
int32 n2=2;
}
message DivideReply {
float f1=1;
}
proto 컴파일
- 다음으로는 앞서 생성한 calc.proto 버퍼를 python 에서 직접 grpc.proto 를 이용하여 컴파일 할 수 있도록 해야 합니다.
- 해당 작업을 통해 Python 코드로 calc.proto 버퍼 내용의 함수들이 자동으로 생성됩니다.
- calc.proto 컴파일 하는 명령어는 아래와 같습니다.
- 참고로 저는 파이썬\protos\calc.proto 경로에 calc.proto 파일을 생성해 놓았습니다.
- 다음 명령어를 실행하게 되면 calc_pb.py, calc_pb2.grpc.py 2개의 파일이 자동 생성됩니다.
python -m grpc_tools.protoc -I./protos --python_out=. --grpc_python_out=. ./protos/calc.proto
calc_pb2.py
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: calc.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ncalc.proto\x12\x04\x63\x61lc\"$\n\nAddRequest\x12\n\n\x02n1\x18\x01 \x01(\x05\x12\n\n\x02n2\x18\x02 \x01(\x05\"\x16\n\x08\x41\x64\x64Reply\x12\n\n\x02n1\x18\x01 \x01(\x05\"*\n\x10SubstractRequest\x12\n\n\x02n1\x18\x01 \x01(\x05\x12\n\n\x02n2\x18\x02 \x01(\x05\"\x1c\n\x0eSubstractReply\x12\n\n\x02n1\x18\x01 \x01(\x05\")\n\x0fMultiplyRequest\x12\n\n\x02n1\x18\x01 \x01(\x05\x12\n\n\x02n2\x18\x02 \x01(\x05\"\x1b\n\rMeuliplyReply\x12\n\n\x02n1\x18\x01 \x01(\x05\"\'\n\rDivideRequest\x12\n\n\x02n1\x18\x01 \x01(\x05\x12\n\n\x02n2\x18\x02 \x01(\x05\"\x19\n\x0b\x44ivideReply\x12\n\n\x02n1\x18\x01 \x01(\x05\x32\xe2\x01\n\nCalculator\x12)\n\x03\x41\x64\x64\x12\x10.calc.AddRequest\x1a\x0e.calc.AddReply\"\x00\x12;\n\tSubstract\x12\x16.calc.SubstractRequest\x1a\x14.calc.SubstractReply\"\x00\x12\x38\n\x08Multiply\x12\x15.calc.MultiplyRequest\x1a\x13.calc.MeuliplyReply\"\x00\x12\x32\n\x06\x44ivide\x12\x13.calc.DivideRequest\x1a\x11.calc.DivideReply\"\x00\x62\x06proto3')
_ADDREQUEST = DESCRIPTOR.message_types_by_name['AddRequest']
_ADDREPLY = DESCRIPTOR.message_types_by_name['AddReply']
_SUBSTRACTREQUEST = DESCRIPTOR.message_types_by_name['SubstractRequest']
_SUBSTRACTREPLY = DESCRIPTOR.message_types_by_name['SubstractReply']
_MULTIPLYREQUEST = DESCRIPTOR.message_types_by_name['MultiplyRequest']
_MEULIPLYREPLY = DESCRIPTOR.message_types_by_name['MeuliplyReply']
_DIVIDEREQUEST = DESCRIPTOR.message_types_by_name['DivideRequest']
_DIVIDEREPLY = DESCRIPTOR.message_types_by_name['DivideReply']
AddRequest = _reflection.GeneratedProtocolMessageType('AddRequest', (_message.Message,), {
'DESCRIPTOR' : _ADDREQUEST,
'__module__' : 'calc_pb2'
# @@protoc_insertion_point(class_scope:calc.AddRequest)
})
_sym_db.RegisterMessage(AddRequest)
AddReply = _reflection.GeneratedProtocolMessageType('AddReply', (_message.Message,), {
'DESCRIPTOR' : _ADDREPLY,
'__module__' : 'calc_pb2'
# @@protoc_insertion_point(class_scope:calc.AddReply)
})
_sym_db.RegisterMessage(AddReply)
SubstractRequest = _reflection.GeneratedProtocolMessageType('SubstractRequest', (_message.Message,), {
'DESCRIPTOR' : _SUBSTRACTREQUEST,
'__module__' : 'calc_pb2'
# @@protoc_insertion_point(class_scope:calc.SubstractRequest)
})
_sym_db.RegisterMessage(SubstractRequest)
SubstractReply = _reflection.GeneratedProtocolMessageType('SubstractReply', (_message.Message,), {
'DESCRIPTOR' : _SUBSTRACTREPLY,
'__module__' : 'calc_pb2'
# @@protoc_insertion_point(class_scope:calc.SubstractReply)
})
_sym_db.RegisterMessage(SubstractReply)
MultiplyRequest = _reflection.GeneratedProtocolMessageType('MultiplyRequest', (_message.Message,), {
'DESCRIPTOR' : _MULTIPLYREQUEST,
'__module__' : 'calc_pb2'
# @@protoc_insertion_point(class_scope:calc.MultiplyRequest)
})
_sym_db.RegisterMessage(MultiplyRequest)
MeuliplyReply = _reflection.GeneratedProtocolMessageType('MeuliplyReply', (_message.Message,), {
'DESCRIPTOR' : _MEULIPLYREPLY,
'__module__' : 'calc_pb2'
# @@protoc_insertion_point(class_scope:calc.MeuliplyReply)
})
_sym_db.RegisterMessage(MeuliplyReply)
DivideRequest = _reflection.GeneratedProtocolMessageType('DivideRequest', (_message.Message,), {
'DESCRIPTOR' : _DIVIDEREQUEST,
'__module__' : 'calc_pb2'
# @@protoc_insertion_point(class_scope:calc.DivideRequest)
})
_sym_db.RegisterMessage(DivideRequest)
DivideReply = _reflection.GeneratedProtocolMessageType('DivideReply', (_message.Message,), {
'DESCRIPTOR' : _DIVIDEREPLY,
'__module__' : 'calc_pb2'
# @@protoc_insertion_point(class_scope:calc.DivideReply)
})
_sym_db.RegisterMessage(DivideReply)
_CALCULATOR = DESCRIPTOR.services_by_name['Calculator']
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
_ADDREQUEST._serialized_start=20
_ADDREQUEST._serialized_end=56
_ADDREPLY._serialized_start=58
_ADDREPLY._serialized_end=80
_SUBSTRACTREQUEST._serialized_start=82
_SUBSTRACTREQUEST._serialized_end=124
_SUBSTRACTREPLY._serialized_start=126
_SUBSTRACTREPLY._serialized_end=154
_MULTIPLYREQUEST._serialized_start=156
_MULTIPLYREQUEST._serialized_end=197
_MEULIPLYREPLY._serialized_start=199
_MEULIPLYREPLY._serialized_end=226
_DIVIDEREQUEST._serialized_start=228
_DIVIDEREQUEST._serialized_end=267
_DIVIDEREPLY._serialized_start=269
_DIVIDEREPLY._serialized_end=294
_CALCULATOR._serialized_start=297
_CALCULATOR._serialized_end=523
# @@protoc_insertion_point(module_scope)
calc_pb2_grpc.py
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import calc_pb2 as calc__pb2
class CalculatorStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Add = channel.unary_unary(
'/calc.Calculator/Add',
request_serializer=calc__pb2.AddRequest.SerializeToString,
response_deserializer=calc__pb2.AddReply.FromString,
)
self.Substract = channel.unary_unary(
'/calc.Calculator/Substract',
request_serializer=calc__pb2.SubstractRequest.SerializeToString,
response_deserializer=calc__pb2.SubstractReply.FromString,
)
self.Multiply = channel.unary_unary(
'/calc.Calculator/Multiply',
request_serializer=calc__pb2.MultiplyRequest.SerializeToString,
response_deserializer=calc__pb2.MeuliplyReply.FromString,
)
self.Divide = channel.unary_unary(
'/calc.Calculator/Divide',
request_serializer=calc__pb2.DivideRequest.SerializeToString,
response_deserializer=calc__pb2.DivideReply.FromString,
)
class CalculatorServicer(object):
"""Missing associated documentation comment in .proto file."""
def Add(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Substract(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Multiply(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Divide(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_CalculatorServicer_to_server(servicer, server):
rpc_method_handlers = {
'Add': grpc.unary_unary_rpc_method_handler(
servicer.Add,
request_deserializer=calc__pb2.AddRequest.FromString,
response_serializer=calc__pb2.AddReply.SerializeToString,
),
'Substract': grpc.unary_unary_rpc_method_handler(
servicer.Substract,
request_deserializer=calc__pb2.SubstractRequest.FromString,
response_serializer=calc__pb2.SubstractReply.SerializeToString,
),
'Multiply': grpc.unary_unary_rpc_method_handler(
servicer.Multiply,
request_deserializer=calc__pb2.MultiplyRequest.FromString,
response_serializer=calc__pb2.MeuliplyReply.SerializeToString,
),
'Divide': grpc.unary_unary_rpc_method_handler(
servicer.Divide,
request_deserializer=calc__pb2.DivideRequest.FromString,
response_serializer=calc__pb2.DivideReply.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'calc.Calculator', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class Calculator(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def Add(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/calc.Calculator/Add',
calc__pb2.AddRequest.SerializeToString,
calc__pb2.AddReply.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Substract(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/calc.Calculator/Substract',
calc__pb2.SubstractRequest.SerializeToString,
calc__pb2.SubstractReply.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Multiply(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/calc.Calculator/Multiply',
calc__pb2.MultiplyRequest.SerializeToString,
calc__pb2.MeuliplyReply.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Divide(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/calc.Calculator/Divide',
calc__pb2.DivideRequest.SerializeToString,
calc__pb2.DivideReply.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
- 이제 Python 으로 gRPC Server/Client 코드를 작성해 보도록 하겠습니다.
Server
from concurrent import futures
import time
import grpc
import calc_pb2
import calc_pb2_grpc
class Calculator(calc_pb2_grpc.CalculatorServicer):
def Add(self, request, context):
return calc_pb2.AddReply(n1=request.n1 + request.n2)
def Substract(self, request, context):
return calc_pb2.SubstractReply(n1=request.n1 - request.n2)
def Multiply(self, request, context):
return calc_pb2.MeuliplyReply(n1=request.n1 * request.n2)
def Divide(self, request, context):
return calc_pb2.DivideReply(f1=request.n1 / request.n2)
def serve():
print("Server Start..")
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
calc_pb2_grpc.add_CalculatorServicer_to_server(Calculator(), server)
server.add_insecure_port('[::]:50050')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
Client
import grpc
import calc_pb2
import calc_pb2_grpc
def run():
channel = grpc.insecure_channel('localhost:50050')
stub = calc_pb2_grpc.CalculatorStub(channel)
respones = stub.Add(calc_pb2.AddRequest(n1=20, n2=10))
print(respones.n1)
respones = stub.Substract(calc_pb2.SubstractRequest(n1=20, n2=10))
print(respones.n1)
respones = stub.Multiply(calc_pb2.MultiplyRequest(n1=20, n2=10))
print(respones.n1)
respones = stub.Divide(calc_pb2.DivideRequest(n1=10, n2=2))
print(respones.f1)
if __name__ == '__main__':
run()
서버 실행
- 서버는 cmd 로 실행하도록 하겠습니다.
- calc_server.py 가 있는 경로로 cmd 실행하고,
python calc_server.py
명령어를 입력하여 Server 를 실행합니다.
C:\Users\Desktop\파이썬>python calc_server.py
Server Start..
클라이언트 실행
- 다음과 같이 덧셈, 뺄셈, 곱셈, 나눗셈의 값이 서버를 통해 계산되어 결과값이 반환되어 출려된 것을 확인할 수 있습니다.
30
10
200
5.0
728x90
'gRPC' 카테고리의 다른 글
[gRPC] C# gRPC Server/Client 생성하기 (0) | 2023.03.21 |
---|---|
[gRPC] gRPC 프로토콜 버퍼 기초 (0) | 2023.03.02 |
[gRPC] gRPC 프로토콜 버퍼 기초 - 메시지 정의 2 (0) | 2023.03.02 |
[gRPC] gRPC 인터셉터 (0) | 2023.03.01 |
[gRPC] .NET Core 에서 gRPC 오류 처리하는 방법 (0) | 2023.03.01 |
이 글을 공유하기