Что такое gRPC и зачем он нужен?
gRPC (gRPC Remote Procedure Calls) — это высокопроизводительный, открытый фреймворк для удаленного вызова процедур (RPC), разработанный компанией Google. Он позволяет клиентскому приложению напрямую вызывать методы на серверном приложении, как если бы это был локальный объект, что упрощает создание распределенных систем и микросервисов.
В основе gRPC лежит Protocol Buffers (protobuf) — мощный механизм сериализации структурированных данных. Вы определяете структуру данных и сервис в .proto файле, а затем компилятор protoc генерирует код на нужных языках (в нашем случае — C++).
Основные преимущества gRPC:
- Высокая производительность: Использует HTTP/2 для мультиплексирования запросов и бинарный формат Protobuf, что значительно быстрее JSON/XML.
- Строгая типизация: Контракты между сервисами четко определены в
.protoфайлах. - Поддержка потоковой передачи: Поддерживает server-streaming, client-streaming и bidirectional-streaming.
- Мультиязычность: Генерирует клиенты и серверы на C++, Java, Python, Go, C#, Ruby и многих других языках.
- Встроенная аутентификация и шифрование: Поддержка TLS/SSL и перехватчиков (interceptors).
Библиотека gRPC для C++ — это нативная реализация, которая дает максимальный контроль над памятью и производительностью, что критично для высоконагруженных систем, игровых серверов, финансовых приложений и IoT.
Установка gRPC для C++
Установка gRPC для C++ — процесс более сложный, чем для Python, так как требует компиляции из исходников или использования менеджера пакетов vcpkg. Мы рассмотрим два основных способа.
Способ 1: Установка через vcpkg (рекомендуется)
vcpkg — это кроссплатформенный менеджер пакетов для C++ от Microsoft.
# 1. Клонируем vcpkg
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
# 2. Устанавливаем vcpkg (Windows: bootstrap-vcpkg.bat)
./bootstrap-vcpkg.sh
# 3. Интегрируем с системой (для Visual Studio или CMake)
./vcpkg integrate install
# 4. Устанавливаем gRPC (вместе с protobuf и зависимостями)
./vcpkg install grpc
После установки в вашем CMakeLists.txt нужно подключить найденный пакет:
find_package(gRPC CONFIG REQUIRED)
find_package(Protobuf CONFIG REQUIRED)
target_link_libraries(my_target
gRPC::grpc++
gRPC::grpc++_reflection
protobuf::libprotobuf
)
Способ 2: Сборка из исходников (Linux/macOS)
Этот способ дает больше контроля, но требует установки зависимостей (cmake, openssl, zlib, abseil).
# Клонируем репозиторий с субмодулями
git clone --recurse-submodules -b v1.62.0 https://github.com/grpc/grpc
cd grpc
# Создаем папку для сборки
mkdir -p cmake/build
cd cmake/build
# Конфигурируем и собираем
cmake ../.. -DgRPC_INSTALL=ON \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/local
make -j$(nproc)
sudo make install
Быстрый старт: Пример "Hello World"
Создадим простое приложение, которое принимает имя пользователя и возвращает приветствие.
Шаг 1: Определяем сервис в .proto файле
Создайте файл hello.proto:
syntax = "proto3";
package helloworld;
// Определяем сообщение для запроса
message HelloRequest {
string name = 1;
}
// Определяем сообщение для ответа
message HelloReply {
string message = 1;
}
// Определяем RPC сервис
service Greeter {
// Унарный RPC: один запрос -> один ответ
rpc SayHello (HelloRequest) returns (HelloReply);
}
Шаг 2: Генерируем C++ код
protoc --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` hello.proto
protoc --cpp_out=. hello.proto
Это создаст файлы: hello.pb.h, hello.pb.cc (для сообщений), hello.grpc.pb.h, hello.grpc.pb.cc (для сервиса).
Шаг 3: Реализуем сервер (server.cpp)
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "hello.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloRequest;
using helloworld::HelloReply;
// Реализация сервиса
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context,
const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello, ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
GreeterServiceImpl 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;
}
Шаг 4: Реализуем клиент (client.cpp)
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "hello.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloRequest;
using helloworld::HelloReply;
class GreeterClient {
public:
GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
std::string SayHello(const std::string& user) {
HelloRequest request;
request.set_name(user);
HelloReply reply;
ClientContext context;
Status status = stub_->SayHello(&context, request, &reply);
if (status.ok()) {
return reply.message();
} else {
std::cerr << "RPC failed: " << status.error_message() << std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv) {
GreeterClient greeter(
grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("World");
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;
return 0;
}
Шаг 5: Компилируем и запускаем
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(HelloWorldGrpc)
find_package(gRPC CONFIG REQUIRED)
find_package(Protobuf CONFIG REQUIRED)
add_executable(server server.cpp hello.pb.cc hello.grpc.pb.cc)
target_link_libraries(server gRPC::grpc++ protobuf::libprotobuf)
add_executable(client client.cpp hello.pb.cc hello.grpc.pb.cc)
target_link_libraries(client gRPC::grpc++ protobuf::libprotobuf)
# Сборка
mkdir build && cd build
cmake ..
make -j4
# Запуск (в разных терминалах)
./server
./client
Основные методы и функции gRPC C++
Ниже приведены ключевые классы и методы, которые вы будете использовать при работе с gRPC в C++.
1. grpc::CreateChannel
Описание: Создает канал (Channel) для подключения к gRPC серверу. Канал — это абстракция соединения, которая может автоматически переподключаться.
Параметры:
target(string): Адрес сервера в формате "host:port".creds(shared_ptr<ChannelCredentials>): Учетные данные для аутентификации (например,InsecureChannelCredentials()для незащищенного соединения).
Пример:
auto channel = grpc::CreateChannel("localhost:50051",
grpc::InsecureChannelCredentials());
// Создаем stub для вызова методов
auto stub = Greeter::NewStub(channel);
2. grpc::ServerBuilder::AddListeningPort
Описание: Добавляет порт, на котором сервер будет принимать входящие соединения.
Параметры:
addr(string): Адрес и порт для прослушивания.creds(ServerCredentials): Учетные данные сервера.selected_port(int*, опционально): Если порт 0, сюда будет записан реально выбранный порт.
Пример:
grpc::ServerBuilder builder;
int port = 0;
builder.AddListeningPort("0.0.0.0:0",
grpc::InsecureServerCredentials(),
&port);
// После BuildAndStart() в port будет реальный номер порта
3. grpc::ServerBuilder::RegisterService
Описание: Регистрирует реализацию сервиса на сервере. Сервер может обслуживать несколько сервисов одновременно.
Параметры:
service(Service*): Указатель на экземпляр класса, унаследованного от сгенерированного Service класса.
Пример:
MyServiceImpl my_service;
builder.RegisterService(&my_service);
// Можно зарегистрировать несколько сервисов
OtherServiceImpl other_service;
builder.RegisterService(&other_service);
4. grpc::ServerBuilder::BuildAndStart
Описание: Строит и запускает сервер. Возвращает уникальный указатель на объект Server. После вызова этого метода сервер начинает принимать запросы.
Параметры: Нет.
Пример:
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
std::cout << "Server started" << std::endl;
server->Wait(); // Блокирует текущий поток до остановки сервера
5. grpc::Server::Wait
Описание: Блокирует текущий поток (обычно main) до тех пор, пока сервер не будет остановлен вызовом Shutdown() из другого потока или обработчика сигналов.
Параметры: Нет.
Пример:
// В потоке обработки сигнала SIGINT
server->Shutdown();
// В main потоке
server->Wait(); // Разблокируется после Shutdown()
6. grpc::ClientContext
Описание: Контекст для одного RPC вызова. Используется для передачи метаданных, таймаутов, отмены вызова и получения информации о статусе.
Ключевые методы:
set_deadline