[Python] Python gRPC 상품 예제
- 파이썬(Python)
- 2023. 2. 23. 02:48
목적
- ProductInfo 서비스 관련 Proto 파일을 생성합니다.
- 생성된 Proto 파일을 토대로, Python gRPC Server/Client 코드 작성 진행하여 gRPC 통신 테스트 진행하였습니다.
메시지 정의
- gRPC 에서 메시지는 클라이언트와 서비스 간에 교환되는 데이터 구조입니다.
- 이번에 예제로 만들 ProductInfo는 2가지의 메시지 타입을 갖습니다.
- 하나는 시스템에 새 제품을 추가하거나 특정 제품을 검색할 때 반환되는 제품 정보이며, 다른 하하는 시스템에서 특정 제품을 검색하거나 새 제품을 추가할 때 반환되는 제품의 고유 식별자 입니다.
ProdcutInfo 프로토콜 버퍼 정의
- prodcut.proto 파일을 생성하여 아래와 같이 메시지를 정의하였습니다.
- addProdcut 는 시스템에서 새 Product 를 등록하는 역할을 합니다.
- getProduct 는 제품 정보를 조회하는 역할을 합니다.
- package 는 프로토콜 버퍼 정의에서 다른 프로젝트와의 이름 충돌을 방지해 주는 역할을 합니다.
syntax = "proto3";
package ecommerce;
service ProductInfo {
rpc addProduct(Product) returns (ProductID);
rpc getProduct(ProductID) returns (Product);
}
message Product {
string id = 1;
string name = 2;
string description = 3;
float price = 4;
}
message ProductID {
string value = 1;
}
Generate gRPC code
- 앞에서 생성한 product.proto 서비스 정의를 사용하기 위해 애플리케이션에서 사용하는 gRPC 코드를 업데이트 진행합니다.
- 진행하는 명령어는 아래와 같습니다.
- product.proto 파일이 있는 위치로 cd 명령을 통해 이동합니다.
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./product.proto
- 해당 명령을 실행하게 되면, product_pb2_grpc.py, product_pb2.py 2개의 파일이 생성됩니다.
product_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 product_pb2 as product__pb2
class ProductInfoStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.addProduct = channel.unary_unary(
'/ecommerce.ProductInfo/addProduct',
request_serializer=product__pb2.Product.SerializeToString,
response_deserializer=product__pb2.ProductID.FromString,
)
self.getProduct = channel.unary_unary(
'/ecommerce.ProductInfo/getProduct',
request_serializer=product__pb2.ProductID.SerializeToString,
response_deserializer=product__pb2.Product.FromString,
)
class ProductInfoServicer(object):
"""Missing associated documentation comment in .proto file."""
def addProduct(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 getProduct(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_ProductInfoServicer_to_server(servicer, server):
rpc_method_handlers = {
'addProduct': grpc.unary_unary_rpc_method_handler(
servicer.addProduct,
request_deserializer=product__pb2.Product.FromString,
response_serializer=product__pb2.ProductID.SerializeToString,
),
'getProduct': grpc.unary_unary_rpc_method_handler(
servicer.getProduct,
request_deserializer=product__pb2.ProductID.FromString,
response_serializer=product__pb2.Product.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'ecommerce.ProductInfo', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class ProductInfo(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def addProduct(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, '/ecommerce.ProductInfo/addProduct',
product__pb2.Product.SerializeToString,
product__pb2.ProductID.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getProduct(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, '/ecommerce.ProductInfo/getProduct',
product__pb2.ProductID.SerializeToString,
product__pb2.Product.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
product_pb2.py
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: product.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\rproduct.proto\x12\tecommerce\"G\n\x07Product\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\r\n\x05price\x18\x04 \x01(\x02\"\x1a\n\tProductID\x12\r\n\x05value\x18\x01 \x01(\t2}\n\x0bProductInfo\x12\x36\n\naddProduct\x12\x12.ecommerce.Product\x1a\x14.ecommerce.ProductID\x12\x36\n\ngetProduct\x12\x14.ecommerce.ProductID\x1a\x12.ecommerce.Productb\x06proto3')
_PRODUCT = DESCRIPTOR.message_types_by_name['Product']
_PRODUCTID = DESCRIPTOR.message_types_by_name['ProductID']
Product = _reflection.GeneratedProtocolMessageType('Product', (_message.Message,), {
'DESCRIPTOR' : _PRODUCT,
'__module__' : 'product_pb2'
# @@protoc_insertion_point(class_scope:ecommerce.Product)
})
_sym_db.RegisterMessage(Product)
ProductID = _reflection.GeneratedProtocolMessageType('ProductID', (_message.Message,), {
'DESCRIPTOR' : _PRODUCTID,
'__module__' : 'product_pb2'
# @@protoc_insertion_point(class_scope:ecommerce.ProductID)
})
_sym_db.RegisterMessage(ProductID)
_PRODUCTINFO = DESCRIPTOR.services_by_name['ProductInfo']
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
_PRODUCT._serialized_start=28
_PRODUCT._serialized_end=99
_PRODUCTID._serialized_start=101
_PRODUCTID._serialized_end=127
_PRODUCTINFO._serialized_start=129
_PRODUCTINFO._serialized_end=254
# @@protoc_insertion_point(module_scope)
Server 구현
- 이제 Python 코드로 gRPC 서버를 구현합니다.
from concurrent import futures
import logging
import uuid
import grpc
import time
import product_pb2
import product_pb2_grpc
class ProductInfoServicer(product_pb2_grpc.ProductInfoServicer):
def __init__(self):
self.productMap = {}
def addProduct(self, request, context):
id = uuid.uuid1()
request.id = str(id)
print("addProduct:request", request)
self.productMap[str(id)] = request
response = product_pb2.ProductID(value = str(id))
print("addProduct:response", response)
return response
def getProduct(self, request, context):
print("getProduct:request", request)
id = request.value
response = self.productMap[str(id)]
print("getProduct:response", response)
return response
# create a gRPC server
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# use the generated function `add_CalculatorServicer_to_server`
# to add the defined class to the server
product_pb2_grpc.add_ProductInfoServicer_to_server(
ProductInfoServicer(), server)
# listen on port 50051
print('Starting server. Listening on port 50051.')
server.add_insecure_port('[::]:50051')
server.start()
# since server.start() will not block,
# a sleep-loop is added to keep alive
try:
while True:
time.sleep(86400)
except KeyboardInterrupt:
server.stop(0)
Client 구현
- 다음으로 Client 를 Python 코드로 구현합니다.
import grpc
import product_pb2
import product_pb2_grpc
import time;
def run():
# open a gRPC channel
channel = grpc.insecure_channel('localhost:50051')
# create a stub (client)
stub = product_pb2_grpc.ProductInfoStub(channel)
response = stub.addProduct(product_pb2.Product(name = "Apple iPhone 11", description = "Meet Apple iPhone 11. All-new dual-camera system with Ultra Wide and Night mode.", price = 699.0 ))
print("add product: response", response)
productInfo = stub.getProduct(product_pb2.ProductID(value = response.value))
print("get product: response", productInfo)
run()
실행 결과
- 아래와 같이 Client 에서 생성한 Product 와 생성된 ProductID 로 조회까지 정상적으로 된 것을 확인할 수 있습니다.
Server 내용
C:\Users\Desktop\gRPC\gRPC\gRPC 상품 예제>python product_server.py
Starting server. Listening on port 50051.
addProduct:request id: "1b1245fd-b012-11ec-9111-9cb6d0fbe574"
name: "Apple iPhone 11"
description: "Meet Apple iPhone 11. All-new dual-camera system with Ultra Wide and Night mode."
price: 699.0
addProduct:response value: "1b1245fd-b012-11ec-9111-9cb6d0fbe574"
getProduct:request value: "1b1245fd-b012-11ec-9111-9cb6d0fbe574"
getProduct:response id: "1b1245fd-b012-11ec-9111-9cb6d0fbe574"
name: "Apple iPhone 11"
description: "Meet Apple iPhone 11. All-new dual-camera system with Ultra Wide and Night mode."
price: 699.0
Client 내용
add product: response value: "1b1245fd-b012-11ec-9111-9cb6d0fbe574"
get product: response id: "1b1245fd-b012-11ec-9111-9cb6d0fbe574"name: "Apple iPhone 11"
description: "Meet Apple iPhone 11. All-new dual-camera system with Ultra Wide and Night mode."
price: 699.0
728x90
'파이썬(Python)' 카테고리의 다른 글
[Python] Dictionary, List Json 파일 만들기 (0) | 2023.03.08 |
---|---|
[Pyhton] Python Convention (코딩규칙) (0) | 2023.03.06 |
[Python] Python gRPC RPC 패턴 (0) | 2023.02.22 |
[Python] 파이썬 - 공백 구분하여 입력받기 (0) | 2023.02.22 |
[Python] 파이썬 도커 파일 및 이미지 만들기 (0) | 2022.04.05 |
이 글을 공유하기