Kubernetes 이야기

Python에서 gRPC 구현 본문

개발/python

Python에서 gRPC 구현

kmaster 2023. 2. 13. 17:15
반응형

최근 어플리케이션이 "모놀리식 아키텍처"에서 "마이크로서비스 아키첵처 (MSA)" 이동하고 있다. 이는 서로 다른 수 많은 마이크로 서비스 간에 상호 작용해야 한다는 뜻이다. 현재 대부분의 서비스들간의 호출은 RESTful API 호출 방식을 가지고 있다.

 

gRPC는 모든 환경에서 실행할 수 있는 최신 오픈 소스 고성능 RPC(원격 프로시저 호출) 프레임워크이다. 로드 밸런싱, 추적, 상태 확인 및 인증을 위한 플러그형 지원을 통해 데이터 센터 안팎에서 서비스를 효율적으로 연결할 수 있다. 

 

 

많은 RPC 시스템에서와 마찬가지로 gRPC는 매개 변수 및 반환 유형을 사용하여 원격으로 호출할 수 있는 메서드를 지정하여 서비스를 정의한다는 아이디어를 기반으로 한다. 서버 측에서 서버는 이 인터페이스를 구현하고 gRPC 서버를 실행하여 클라이언트 호출을 처리한다. 클라이언트 쪽에서 클라이언트에는 서버와 동일한 메서드를 제공하는 스텁(일부 언어에서는 클라이언트라고 함)이 있다.

 

gRPC 특징

  • HTTP/2 기반 전송
  • 인증, 추적, 로드 밸런싱 및 상태 확인
  • 프로토콜 버퍼 사용

기본적으로 gRPC는 프로토콜 버퍼를 사용한다. 프로토콜 버퍼는 구조화된 데이터를 직렬화하기 위한 언어 중립적이고 플랫폼 중립적인 확장 가능한 메커니즘이다.

프로토콜 버퍼는 정의 언어(.proto 파일로 생성됨), proto 컴파일러가 데이터와 인터페이스하기 위해 생성하는 코드, 언어별 런타임 라이브러리, 파일에 작성되거나 전송되는 데이터의 직렬화 형식의 조합이다.

 

프로토콜 버퍼 특징

프로토콜 버퍼는  다음과 같은 점에서 XML본다 낫다고 할 수 있다.

  • 심플
  • 3~10배 작음
  • 20~100배 더 빠름
  • 프로그래밍 방식으로 더 쉽게 사용할 수 있도록 데이터 액세스 클래스를 생성

gRPC는 다음과 같은 다양한 방법으로 활용할 수 있다.

 

  • 마이크로서비스 스타일 아키텍처에서 다국어 서비스를 효율적으로 연결
  • 모바일 장치, 브라우저 클라이언트를 백엔드 서비스에 연결
  • 효율적인 클라이언트 라이브러리 생성

gRPC 구현 유형

 

1) 단방향 RPC

rpc HelloServer(RequestMessage) returns (ResponseMessage);

2) 양방향 스트리밍 RPC

rpc HelloServer(stream RequestMessage) returns (stream ResponseMessage);

3) 클라이언트 스트링 RPC

rpc HelloServer(stream RequestMessage) returns (ResponseMessage);

4) 서버 스트리밍 RPC

rpc HelloServer(RequestMessage) returns (stream ResponseMessage);

 

Python에서 gRPC 구현

 

gRPC 환경 세팅

pipenv --python 3.11
pipenv shell
pip install grpcio grpcio-tools

 

단방향 gRPC

 

gRPC 서비스를 구현하려면 3개의 파일이 필요하다.

  • Proto 파일 : Proto 파일은 스텁(<package_name>_pb2.py 및 <package_name>_pb2_grpc.py)을 생성하는 데 사용되는 서비스 선언으로 구성된다. 이 파일은 gRPC 서버와 클라이언트에서 사용된다.
  • gRPC 서버 : 클라이언트 요청에 대한 응답을 제공한다.
  • gRPC 클라이언트 : 서버에 gRPC를 호출하여 proto 파일에 따라 응답을 받는다.

먼저 hello.proto 파일을 먼저 생성해보자.

syntax = "proto3";

package hello;

service Hello{
 rpc GetServerResponse(Message) returns (MessageResponse) {}

}

message Message{
 string message = 1;
}

message MessageResponse{
 string message = 1;
 bool received = 2;
}

이제 proto 파일을 이용하여 stub 을 생성한다.

python -m grpc_tools.protoc --python_out=. --grpc_python_out=. --proto_path=. ./hello.proto

이렇게 stub을 생성하면 2개의 파일이 생성된다.

hello_pb2.py  hello_pb2_grpc.py

 

이 2개이 Stub 파일을 이용하여 서버와 클라이언트를 구현해 보자.

 

서버

import grpc
from concurrent import futures
import time
import hello_pb2_grpc as pb2_grpc
import hello_pb2 as pb2


class HelloService(pb2_grpc.HelloServicer):

    def __init__(self, *args, **kwargs):
        pass

    def GetServerResponse(self, request, context):

        message = request.message
        result = f'Hello I am up and running received "{message}" message from you'
        result = {'message': result, 'received': True}

        return pb2.MessageResponse(**result)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=2))
    pb2_grpc.add_HelloServicer_to_server(HelloService(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    server.wait_for_termination()


if __name__ == '__main__':
    serve()

생성한 서버를 구동한다.

python hello_server.py

 

클라이언트

import grpc
import hello_pb2_grpc as pb2_grpc
import hello_pb2 as pb2


class HelloClient(object):
    def __init__(self):
        self.host = 'localhost'
        self.server_port = 50051

        self.channel = grpc.insecure_channel(
            '{}:{}'.format(self.host, self.server_port))

        self.stub = pb2_grpc.HelloStub(self.channel)

    def get_url(self, message):
        message = pb2.Message(message=message)
        print(f'{message}')
        return self.stub.GetServerResponse(message)


if __name__ == '__main__':
    client = HelloClient()
    result = client.get_url(message="Hello Server you there?")
    print(f'{result}')

 

클라이언트를 실행하면 다음과 같은 결과를 볼 수 있다.

c# python hello_client.py
message: "Hello Server you there?"

message: "Hello I am up and running received \"Hello Server you there?\" message from you"
received: true

 

양방향 구현

syntax = "proto3";

package community;

service Community {
   rpc GetServerResponse(stream Message) returns (stream Message) {}
}

message Message {
  string message = 1;
}

Stub 생성

python -m grpc_tools.protoc --python_out=. --grpc_python_out=. --proto_path=.  ./community.proto

 

서버

from concurrent import futures

import grpc
import community_pb2_grpc as community_pb2_grpc

class CommunityService(community_pb2_grpc.CommunityServicer):

    def GetServerResponse(self, request_iterator, context):
        for message in request_iterator:
            yield message

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=2))
    community_pb2_grpc.add_CommunityServicer_to_server(CommunityService(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    server.wait_for_termination()

if __name__ == '__main__':
    serve()

 

클라이언트

from __future__ import print_function

import grpc
import community_pb2_grpc as community_pb2_grpc
import community_pb2 as community_pb2

def make_message(message):
    return community_pb2.Message(
        message=message
    )

def generate_messages():
    messages = [
        make_message("First message"),
        make_message("Second message"),
        make_message("Third message"),
        make_message("Fourth message"),
        make_message("Fifth message"),
    ]
    for msg in messages:
        print("Hello Server Sending you the %s" % msg.message)
        yield msg

def send_message(stub):
    responses = stub.GetServerResponse(generate_messages())
    for response in responses:
        print("Hello from the server received your %s" % response.message)

def run():
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = community_pb2_grpc.CommunityStub(channel)
        send_message(stub)

if __name__ == '__main__':
    run()

 

[실행결과]

Hello Server Sending you the First message
Hello Server Sending you the Second message
Hello Server Sending you the Third message
Hello Server Sending you the Fourth message
Hello Server Sending you the Fifth message
Hello from the server received your First message
Hello from the server received your Second message
Hello from the server received your Third message
Hello from the server received your Fourth message
Hello from the server received your Fifth message
반응형

'개발 > python' 카테고리의 다른 글

Playwright와 Python 사용 방법  (0) 2023.02.22
playwright를 활용한 e2e 테스트  (0) 2023.02.15
Kopf ( 예제 )  (1) 2023.02.05
Kopf (소개)  (0) 2023.02.03
Ubuntu에서 pipenv 실행 시 FileNotFoundError 오류  (0) 2023.01.28
Comments