Введение в микросервисы на C++
Микросервисная архитектура (MSA) — один из доминирующих подходов к построению современных распределённых систем. Несмотря на то, что C++ реже ассоциируется с микросервисами, чем Go, Java или Python, он остаётся незаменимым для сценариев, где критичны производительность, низкая задержка и эффективное использование памяти. В этой статье мы разберём, как строить микросервисы на C++, какие фреймворки и библиотеки использовать, и рассмотрим практические примеры кода.
Почему C++ для микросервисов?
Основные преимущества C++ при разработке микросервисов:
- Максимальная производительность — C++ позволяет достичь минимального времени отклика (latency) и высокой пропускной способности (throughput).
- Контроль над ресурсами — ручное управление памятью и оптимизация на уровне железа.
- Зрелые библиотеки — Boost, Poco, cpp-httplib, gRPC, Crow.
- Интеграция с legacy-системами — часто C++ используется в ядрах высоконагруженных систем (финансы, телеком, игры).
Архитектура микросервиса на C++
Основные компоненты
Типичный микросервис на C++ включает:
- HTTP/REST сервер — для приёма запросов (например, с использованием
cpp-httplibилиCrow). - Бизнес-логика — модули обработки данных.
- Клиент для межсервисного взаимодействия — gRPC или HTTP-клиент.
- Работа с хранилищем — подключение к PostgreSQL, Redis или Kafka через библиотеки (libpqxx, redis-plus-plus, librdkafka).
Коммуникация между сервисами
Два основных протокола:
- gRPC — бинарный протокол на основе Protobuf, обеспечивает строгую типизацию и высокую скорость.
- REST (JSON) — удобен для внешних API, но медленнее из-за сериализации.
Практика: создание микросервиса на C++
1. Простой REST API с использованием cpp-httplib
Рассмотрим минимальный сервис, который принимает GET-запрос и возвращает JSON. Библиотека cpp-httplib — header-only, её легко подключить.
// main.cpp
#include <httplib.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main() {
httplib::Server svr;
// GET /hello — возвращает JSON
svr.Get("/hello", [](const httplib::Request& req, httplib::Response& res) {
json response;
response["message"] = "Hello from C++ microservice!";
response["status"] = "ok";
res.set_content(response.dump(), "application/json");
});
// POST /data — принимает JSON и обрабатывает
svr.Post("/data", [](const httplib::Request& req, httplib::Response& res) {
try {
auto input = json::parse(req.body);
int value = input["value"];
json result;
result["squared"] = value * value;
res.set_content(result.dump(), "application/json");
} catch (const std::exception& e) {
res.status = 400;
res.set_content("{\"error\":\"Invalid JSON\"}", "application/json");
}
});
std::cout << "Server started on port 8080\n";
svr.listen("0.0.0.0", 8080);
return 0;
}
Сборка (требуется установленный nlohmann/json):
g++ -std=c++17 main.cpp -o microservice -lpthread
./microservice
2. Микросервис с gRPC
gRPC требует определения сервиса в .proto файле. Пример:
// calculator.proto
syntax = "proto3";
service Calculator {
rpc Add (AddRequest) returns (AddResponse);
}
message AddRequest {
int32 a = 1;
int32 b = 2;
}
message AddResponse {
int32 result = 1;
}
Генерация кода и реализация сервера на C++:
// server.cpp
#include <grpcpp/grpcpp.h>
#include "calculator.grpc.pb.h"
class CalculatorServiceImpl final : public Calculator::Service {
grpc::Status Add(grpc::ServerContext* context,
const AddRequest* request,
AddResponse* reply) override {
int32_t sum = request->a() + request->b();
reply->set_result(sum);
return grpc::Status::OK;
}
};
int main() {
CalculatorServiceImpl service;
grpc::ServerBuilder builder;
builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials());
builder.RegisterService(&service);
auto server = builder.BuildAndStart();
std::cout << "gRPC server running on port 50051\n";
server->Wait();
return 0;
}
3. Асинхронная обработка с Boost.Asio
Для высоконагруженных систем используйте асинхронный HTTP-сервер на Boost.Beast (основан на Boost.Asio). Пример минимального сервера:
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio.hpp>
#include <iostream>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
int main() {
try {
net::io_context ioc{1};
tcp::acceptor acceptor{ioc, {tcp::v4(), 8080}};
while (true) {
tcp::socket socket{ioc};
acceptor.accept(socket);
beast::flat_buffer buffer;
http::request<http::string_body> req;
http::read(socket, buffer, req);
http::response<http::string_body> res{http::status::ok, req.version()};
res.set(http::field::server, "Boost.Beast");
res.set(http::field::content_type, "text/plain");
res.body() = "Hello from Boost.Beast!";
res.prepare_payload();
http::write