본문 바로가기
-----ETC-----/C++ 고급 프로그래밍과 응용 프로젝트 시리즈

[C++ 고급 프로그래밍과 응용 프로젝트 시리즈] Day 24: gRPC를 이용한 분산 시스템

by cogito21_cpp 2024. 8. 1.
반응형

gRPC 소개

gRPC는 Google에서 개발한 고성능, 범용 RPC(Remote Procedure Call) 프레임워크입니다. 이를 통해 서로 다른 시스템 간의 통신을 쉽게 구현할 수 있습니다. gRPC는 HTTP/2를 기반으로 하며, 프로토콜 버퍼(Protocol Buffers)를 사용하여 효율적인 직렬화 및 역직렬화를 제공합니다.

 

gRPC 설치

gRPC와 Protocol Buffers를 설치하려면 다음 명령어를 사용합니다.

sudo apt-get install -y build-essential autoconf libtool pkg-config
git clone -b v1.39.1 https://github.com/grpc/grpc
cd grpc
git submodule update --init
make
sudo make install
cd third_party/protobuf
sudo make install

 

gRPC 프로젝트 생성

gRPC를 사용하여 간단한 분산 시스템을 구축합니다. 여기서는 간단한 계산기 서버와 클라이언트를 구현합니다.

 

1. Protocol Buffers 파일 정의

프로젝트 루트에 calculator.proto 파일을 생성하고 다음 내용을 추가합니다.

syntax = "proto3";

package calculator;

service Calculator {
    rpc Add (AddRequest) returns (AddResponse);
    rpc Subtract (SubtractRequest) returns (SubtractResponse);
}

message AddRequest {
    int32 a = 1;
    int32 b = 2;
}

message AddResponse {
    int32 result = 1;
}

message SubtractRequest {
    int32 a = 1;
    int32 b = 2;
}

message SubtractResponse {
    int32 result = 1;
}

 

2. Protocol Buffers 파일 컴파일

다음 명령어를 사용하여 Protocol Buffers 파일을 컴파일합니다.

protoc -I=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` calculator.proto
protoc -I=. --cpp_out=. calculator.proto

 

이 명령어는 calculator.grpc.pb.hcalculator.pb.h 헤더 파일을 생성합니다.

 

gRPC 서버 구현

gRPC 서버를 구현합니다.

 

1. CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(gRPCExample)

set(CMAKE_CXX_STANDARD 17)

find_package(gRPC CONFIG REQUIRED)
find_package(Protobuf CONFIG REQUIRED)

add_executable(calculator_server
    calculator_server.cpp
    calculator.grpc.pb.cc
    calculator.pb.cc
)

target_link_libraries(calculator_server
    gRPC::grpc++_unsecure
    protobuf::libprotobuf
)

 

2. calculator_server.cpp

#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "calculator.grpc.pb.h"

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using calculator::Calculator;
using calculator::AddRequest;
using calculator::AddResponse;
using calculator::SubtractRequest;
using calculator::SubtractResponse;

class CalculatorServiceImpl final : public Calculator::Service {
    Status Add(ServerContext* context, const AddRequest* request, AddResponse* response) override {
        int32_t result = request->a() + request->b();
        response->set_result(result);
        return Status::OK;
    }

    Status Subtract(ServerContext* context, const SubtractRequest* request, SubtractResponse* response) override {
        int32_t result = request->a() - request->b();
        response->set_result(result);
        return Status::OK;
    }
};

void RunServer() {
    std::string server_address("0.0.0.0:50051");
    CalculatorServiceImpl service;

    ServerBuilder builder;
    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
    builder.RegisterService(&service);
    std::unique_ptr<Server> server(builder.BuildAndStart());
    std::cout << "Server listening on " << server_address << std::endl;
    server->Wait();
}

int main(int argc, char** argv) {
    RunServer();
    return 0;
}

 

 

gRPC 클라이언트 구현

gRPC 클라이언트를 구현합니다.

 

1. CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(gRPCExample)

set(CMAKE_CXX_STANDARD 17)

find_package(gRPC CONFIG REQUIRED)
find_package(Protobuf CONFIG REQUIRED)

add_executable(calculator_client
    calculator_client.cpp
    calculator.grpc.pb.cc
    calculator.pb.cc
)

target_link_libraries(calculator_client
    gRPC::grpc++_unsecure
    protobuf::libprotobuf
)

 

2. calculator_client.cpp

#include <iostream>
#include <memory>
#include <grpcpp/grpcpp.h>
#include "calculator.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using calculator::Calculator;
using calculator::AddRequest;
using calculator::AddResponse;
using calculator::SubtractRequest;
using calculator::SubtractResponse;

class CalculatorClient {
public:
    CalculatorClient(std::shared_ptr<Channel> channel) : stub_(Calculator::NewStub(channel)) {}

    int32_t Add(int32_t a, int32_t b) {
        AddRequest request;
        request.set_a(a);
        request.set_b(b);

        AddResponse response;
        ClientContext context;

        Status status = stub_->Add(&context, request, &response);

        if (status.ok()) {
            return response.result();
        } else {
            std::cerr << "RPC failed" << std::endl;
            return -1;
        }
    }

    int32_t Subtract(int32_t a, int32_t b) {
        SubtractRequest request;
        request.set_a(a);
        request.set_b(b);

        SubtractResponse response;
        ClientContext context;

        Status status = stub_->Subtract(&context, request, &response);

        if (status.ok()) {
            return response.result();
        } else {
            std::cerr << "RPC failed" << std::endl;
            return -1;
        }
    }

private:
    std::unique_ptr<Calculator::Stub> stub_;
};

int main(int argc, char** argv) {
    CalculatorClient client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));

    int32_t a = 10;
    int32_t b = 20;

    int32_t sum = client.Add(a, b);
    std::cout << "Sum: " << sum << std::endl;

    int32_t difference = client.Subtract(a, b);
    std::cout << "Difference: " << difference << std::endl;

    return 0;
}

 

 

빌드 및 실행

1. 서버 빌드 및 실행

mkdir build
cd build
cmake ..
make
./calculator_server

 

2. 클라이언트 빌드 및 실행

mkdir build
cd build
cmake ..
make
./calculator_client

 

이제 클라이언트에서 서버로 RPC 호출을 보내고, 서버에서 요청을 처리하여 결과를 반환합니다.

 

이제 24일차의 학습을 마쳤습니다. gRPC를 사용하여 간단한 분산 시스템을 구축하고 서버와 클라이언트를 구현해보았습니다.

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "CMake를 이용한 빌드 시스템 관리"에 대해 학습하겠습니다.

반응형