为什么C++微服务与网络不友好(以及service mesh如何解决这个问题)
想象一下:您构建了一个完美的C++流水线。每个微服务运行飞快,内存管理严格,速度超乎想象。但一旦您将所有内容部署到Kubernetes中,噩梦就开始了。超时、重试、连接中断、数据包丢失。您深入代码,在每个角落插入curl,编写自己的重试轮子——项目变成了泥潭。
熟悉吗?欢迎来到分布式系统的世界。好消息是:service mesh解决了这个问题。坏消息是:大多数教程都是为Go或Java编写的。而我们这些C++开发者该怎么办?我们不想引入笨重的Istio和Envoy,如果我们可以构建一个轻量级且易于理解的解决方案呢?
在本文中,我们将探讨如何无痛地将service mesh集成到C++微服务中,且无需多余的依赖。您将了解什么是sidecar代理,如何配置负载均衡和可观测性,并获取一个使用gRPC的C++真实代码示例。
什么是service mesh,为什么C++项目需要它?
Service mesh是一个专用的基础设施层,用于管理微服务之间的网络流量。您无需将超时、重试、负载均衡和加密逻辑嵌入到每个服务中,而是将其提取到一个代理容器(sidecar)中,该容器与每个服务实例一起运行。
这对C++尤其重要,因为:
- C++代码通常是为高负载系统编写的,每一微秒都至关重要;
- 内嵌的网络逻辑使维护和测试变得复杂;
- C++标准库不提供现成的service mesh机制(与拥有标准HTTP客户端的Go不同)。
Service mesh的主要组件:
- 数据平面——代理服务器(通常是Envoy,但也可以使用您自己的C++实现);
- 控制平面——管理层(例如Istio、Consul或自定义实现)。
我们将专注于数据平面,因为正是在这里C++展现了其强大之处。
用C++编写自己的sidecar代理(最小示例)
假设我们有两个微服务:auth_service和user_service。它们通过gRPC通信。没有service mesh,每个服务必须自行处理重连、超时和指标。有了代理,则无需如此。
以下是一个使用libcurl和nlohmann/json库的C++ sidecar代理最小示例。它接收HTTP请求,将其代理到后端,添加重试机制并记录延迟。
// proxy.cpp#include <httplib.h>#include <nlohmann/json.hpp>#include <curl/curl.h>#include <chrono>#include <thread>
using json = nlohmann::json;using namespace std::chrono_literals;
class SidecarProxy {public: SidecarProxy(const std::string& backend_host, int backend_port, int max_retries = 3) : backend_host_(backend_host), backend_port_(backend_port), max_retries_(max_retries) {}
void handle_request(const httplib::Request& req, httplib::Response& res) { auto start = std::chrono::high_resolution_clock::now(); CURL* curl = curl_easy_init(); std::string url = "http://" + backend_host_ + ":" + std::to_string(backend_port_) + req.path; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
std::string response_string; curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, +[](void* ptr, size_t size, size_t nmemb, void* stream) { ((std::string*)stream)->append((char*)ptr, size * nmemb); return size * nmemb; }); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);
int retries = 0; CURLcode code; d