[Python] Python gRPC 상품 예제

목적

  • 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

이 글을 공유하기

댓글

Designed by JB FACTORY