Einleitung: Warum C++ für Mikroservices?
Die Mikroservice-Architektur hat sich in der Entwicklungsbranche fest etabliert, und obwohl die meisten Projekte in Go, Java oder Python geschrieben werden, bleibt C++ unverzichtbar für Systeme, bei denen Latenz (latency-sensitive) und Hardware-nahe Leistung entscheidend sind. Handelsplattformen, Spieleserver, Echtzeit-Videoverarbeitung – hier sind C++-Mikroservices überlegen.
Allerdings ist die Entwicklung von Mikroservices in C++ schwieriger: Es gibt keine integrierte Laufzeitumgebung wie in der JVM, und Speicherverwaltung, Build-Prozess und Netzwerkkommunikation müssen manuell erfolgen. In diesem Artikel behandeln wir fortgeschrittene Architektur, Werkzeuge und praktische Techniken für Produktionssysteme.
1. Architekturmuster für C++-Mikroservices
1.1. Ereignisbus und asynchrone Kommunikation
In C++ werden traditionell Threads (std::thread) und asynchrone Warteschlangen verwendet. Auf fortgeschrittenem Niveau sollten Bibliotheken eingesetzt werden, die reaktive Programmierung implementieren, wie z.B. libuv oder boost::asio. Sie ermöglichen den Aufbau nicht-blockierender Dienste ohne unnötigen Thread-Overhead.
Beispiel eines asynchronen TCP-Servers mit boost::asio:
#include <boost/asio.hpp>#include <iostream>
using boost::asio::ip::tcp;
void handle_request(tcp::socket& socket) { boost::asio::streambuf buffer; boost::asio::read_until(socket, buffer, ""); std::string data = boost::asio::buffer_cast<const char*>(buffer.data()); // Anfrage verarbeiten boost::asio::write(socket, boost::asio::buffer("Response"));}
int main() { boost::asio::io_context io_context; tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 8080)); while (true) { tcp::socket socket(io_context); acceptor.accept(socket); std::thread(handle_request, std::move(socket)).detach(); } return 0;}1.2. gRPC und Protobuf für die Dienstkommunikation
REST über HTTP ist ein langsames und ineffizientes Protokoll für C++. Verwenden Sie stattdessen gRPC über HTTP/2. Es unterstützt Streaming, bidirektionale Kommunikation und strenge Typisierung durch Protocol Buffers.
Beispiel einer Dienstdefinition in .proto:
syntax = "proto3";
service OrderService { rpc CreateOrder (OrderRequest) returns (OrderResponse); rpc StreamOrders (Empty) returns (stream Order);}
message OrderRequest { string user_id = 1; double amount = 2;}Der generierte C++-Code bietet strenge Typisierung und Serialisierung in ein Binärformat, das 10-20 mal schneller als JSON ist.
2. Zustandsverwaltung und Message Queues
2.1. Eingebettete Datenbanken: RocksDB
Für Mikroservices, bei denen Lese-/Schreibgeschwindigkeit wichtig ist, verwenden Sie RocksDB – einen eingebetteten Key-Value-Store von Facebook. Er eignet sich ideal für Caching, lokale Warteschlangen und die Speicherung von Zuständen innerhalb eines Prozesses.
#include <rocksdb/db.h>#include <iostream>
int main() { rocksdb::DB* db; rocksdb::Options options; options.create_if_missing = true; rocksdb::Status s = rocksdb::DB::Open(options, "/tmp/test_db", &db); if (!s.ok()) return 1;
db->Put(rocksdb::WriteOptions(), "key1", "value1"); std::string value; db->Get(rocksdb::ReadOptions(), "key1", &value); std::cout << value << std::endl;
delete db; return 0;}2.2. Message Queues: RabbitMQ über AMQP-CPP
Für die zuverlässige Zustellung von Ereignissen zwischen Diensten verwenden Sie Warteschlangen. Die Bibliothek AMQP-CPP bietet eine C++-API für RabbitMQ mit Unterstützung für asynchrone Aufrufe.
Beispiel für das Veröffentlichen einer Nachricht:
#include <amqpcpp.h>#include <amqpcpp/linux_tcp.h>
int main() { AMQP::TcpConnection conn