Что такое gRPC?

Ответ

gRPC — это высокопроизводительный фреймворк удаленного вызова процедур (RPC) с открытым исходным кодом, изначально разработанный в Google. Он основан на HTTP/2 и использует Protocol Buffers (protobuf) в качестве языка описания интерфейсов (IDL) и формата сериализации.

Ключевые преимущества для C++ разработки:

  • Производительность: Бинарный протокол protobuf и мультиплексирование потоков в HTTP/2 обеспечивают низкую задержку и высокую пропускную способность.
  • Строгая типизация: Интерфейсы и структуры данных определяются в .proto-файлах, что исключает ошибки несоответствия типов между клиентом и сервером.
  • Поддержка потоковой передачи: Помимо простых запросов-ответов, поддерживаются однонаправленные и двунаправленные потоки.

Пример: простой сервис на C++

  1. Определение сервиса (greeter.proto):

    syntax = "proto3";
    package helloworld;
    
    service Greeter {
      rpc SayHello (HelloRequest) returns (HelloReply) {}
    }
    
    message HelloRequest {
      string name = 1;
    }
    
    message HelloReply {
      string message = 1;
    }
  2. Реализация сервера (упрощенно):

    #include <grpcpp/grpcpp.h>
    #include "greeter.grpc.pb.h"
    
    class GreeterServiceImpl final : public helloworld::Greeter::Service {
      grpc::Status SayHello(grpc::ServerContext* context,
                            const helloworld::HelloRequest* request,
                            helloworld::HelloReply* reply) override {
        std::string prefix("Hello, ");
        reply->set_message(prefix + request->name());
        return grpc::Status::OK;
      }
    };
    
    void RunServer() {
      std::string server_address("0.0.0.0:50051");
      GreeterServiceImpl service;
      grpc::ServerBuilder builder;
      builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
      builder.RegisterService(&service);
      std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
      server->Wait();
    }
  3. Клиентский вызов (упрощенно):

    auto channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
    std::unique_ptr<helloworld::Greeter::Stub> stub = helloworld::Greeter::NewStub(channel);
    
    helloworld::HelloRequest request;
    request.set_name("World");
    helloworld::HelloReply reply;
    grpc::ClientContext context;
    grpc::Status status = stub->SayHello(&context, request, &reply);
    if (status.ok()) {
      std::cout << reply.message() << std::endl;
    }

Типичное применение: микросервисные архитектуры, межпроцессное взаимодействие (IPC), клиент-серверные приложения, где важны низкая задержка и эффективность.

Ответ 18+ 🔞

А, gRPC! Ну это, блядь, такая штука, чтобы сервисы между собой болтали, как будто они в одном процессе сидят, а на самом деле — по сети, через всю эту интернет-хуйню. Google её придумал, там умные дядьки, ёпта. Главный козырь — скорость, просто овердохуища быстрая, потому что под капотом у неё HTTP/2 и бинарные протоколы буферов, а не эта ваша JSON-писанина, которая весит, как хуй с горы.

Чем она для C++ плюшевая:

  • Скорость пиздец: Всё бинарное, сжатое, да ещё и несколько запросов в одном соединении гонять можно. Не то что эти REST'ы, которые для каждой ерунды новое соединение открывают — терпения ноль ебать.
  • Контракты железные: Ты сперва в .proto-файле описываешь, какие методы будут и какие структуры данных летать. Потом компилятор тебе нагенерит код, и всё — никаких «ой, а я думал, поле userName называется, а не username». Доверия ебать ноль к ручному парсингу, зато тут — строгая типизация, красота.
  • Не только туда-сюда: Можно не просто запрос-ответ, а целые потоки данных в одну сторону или даже в обе сразу гнать. Очень удобно, когда надо, например, чат сделать или лог-стрим пулять.

Смотри, как это примерно выглядит, на пальцах:

  1. Пишем контракт (greeter.proto): Тут всё просто, как три копейки. Объявляем сервис Greeter с одним методом SayHello. Он принимает сообщение HelloRequest с именем и отдаёт HelloReply с приветствием. Э бошка думай — логика же элементарная.

    syntax = "proto3";
    package helloworld;
    
    service Greeter {
      rpc SayHello (HelloRequest) returns (HelloReply) {}
    }
    
    message HelloRequest {
      string name = 1;
    }
    
    message HelloReply {
      string message = 1;
    }
  2. Делаем сервер (вот, смотри код): Берём сгенерированный класс, наследуемся, переопределяем метод. Пришёл запрос — достаём имя, лепим к нему «Hello, » и отправляем назад. В рот меня чих-пых, да тут и думать нечего.

    #include <grpcpp/grpcpp.h>
    #include "greeter.grpc.pb.h"
    
    class GreeterServiceImpl final : public helloworld::Greeter::Service {
      grpc::Status SayHello(grpc::ServerContext* context,
                            const helloworld::HelloRequest* request,
                            helloworld::HelloReply* reply) override {
        std::string prefix("Hello, ");
        reply->set_message(prefix + request->name());
        return grpc::Status::OK;
      }
    };
    
    void RunServer() {
      std::string server_address("0.0.0.0:50051");
      GreeterServiceImpl service;
      grpc::ServerBuilder builder;
      builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
      builder.RegisterService(&service);
      std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
      server->Wait();
    }
  3. Клиент, который дергает сервер: Создаём канал к серверу, делаем «стаб» (это такая заглушка-клиент), набиваем запрос данными и просто вызываем метод, как будто он локальный! Ни хуя себе, правда? А вся сетевая магия происходит под капотом.

    auto channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
    std::unique_ptr<helloworld::Greeter::Stub> stub = helloworld::Greeter::NewStub(channel);
    
    helloworld::HelloRequest request;
    request.set_name("World");
    helloworld::HelloReply reply;
    grpc::ClientContext context;
    grpc::Status status = stub->SayHello(&context, request, &reply);
    if (status.ok()) {
      std::cout << reply.message() << std::endl;
    }

Где эту радость применяют? Да везде, где сервисы друг другу не дают покоя. Микросервисы, которые общаются чаще, чем соседки у подъезда, всякие высоконагруженные штуки, где каждая миллисекунда на счету, или даже просто замена своему корявому IPC. В общем, ебать мои старые костыли, хорошая технология.