1. 概述
Envoy 使用单进程多线程架构。一个单独的主线程(primary thread)控制各种零星的协调任务,而多个工作线程(worker threads)负责执行监听、过滤和转发操作。一旦连接被监听器接受,该连接的整个生命周期将绑定到单个工作线程,这使得 Envoy 的大部分代码是单线程的(令人尴尬的并行),只有少量复杂代码负责处理工作线程之间的协调。
Envoy 被设计为 100% 无阻塞的,对于大多数工作负载,建议将工作线程数量配置为等于机器上的硬件线程数。
2. 多线程架构核心组件
2.1 架构概述
┌─────────────────────────────────────────────────────────────────┐│ Envoy 多线程架构 │└─────────────────────────────────────────────────────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐│ 主线程 │ │ 工作线程 │ │ 任务调度 │└──────────┬───────┘ └──────────┬───────┘ └──────────┬───────┘ │ │ │ │ 1. 初始化和启动 │ │ │───────────────────────▶│ │ │ │ │ │ │ 2. 工作线程初始化 │ │ │───────────────────────▶│ │ │ │ │ │ 3. 监听器管理 │ │ │───────────────────────▶│ │ │ │ │ │ 4. 连接接受和处理 │ │ │───────────────────────▶│ │ │ │ │ │ 5. 任务调度和通信 │ │ │───────────────────────▶│ │ │ │ │ 6. 统计信息收集和更新 │ │ │◀──────────────────────┴──────────────────────┘2.2 核心组件 ER 图
┌───────────────────────────────────────────────────────────────────────────────────────────┐│ 多线程核心组件 ER 图 │└───────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐│ MainThreadImpl │ │ WorkerImpl │ │ ConnectionHandlerImpl │└──────────┬───────┘ └──────────┬───────┘ └──────────┬───────┘ │ │ │ │ 1 * │ │ * │ │ │┌──────────▼──────────┐ ┌────▼──────────┐ ┌────▼──────────┐│ ServerImpl │ │ Dispatcher │ │ ActiveTcpListener │└──────────┬──────────┘ └───────────────┘ └───────────────┘ │ │ │ │ 1 1 │ │ 1 │ │ │┌──────────▼──────────┐ ┌────▼──────────┐ ┌────▼──────────┐│ ThreadLocalStorage │ │ ConnectionImpl │ │ Network::ServerConnection │└──────────┬──────────┘ └───────────────┘ └───────────────┘ │ │ │ │ 1 * │ │ * │ │ │┌──────────▼──────────┐ ┌────▼──────────┐ ┌────▼──────────┐│ ScopeManager │ │ HttpConnectionManagerImpl │ │ FilterChain │└──────────┬──────────┘ └───────────────┘ └───────────────┘ │ │ │ │ 1 1 │ │ 1 │ │ │┌──────────▼──────────┐ ┌────▼──────────┐ ┌────▼──────────┐│ ClusterManagerImpl │ │ RouterImpl │ │ FilterManager │└──────────────────────┘ └───────────────┘ └───────────────┘3. 主线程和工作线程关系
3.1 线程启动和初始化
// 服务器初始化过程void ServerImpl::initialize() { // 主线程初始化 main_thread_ = std::make_unique<MainThreadImpl>(*this);
// 创建工作线程 workers_.reserve(worker_count_); for (uint32_t i = 0; i < worker_count_; ++i) { workers_.emplace_back(std::make_unique<WorkerImpl>(*this, i)); }
// 初始化监听器 listener_manager_->initialize();
// 启动主事件循环 run();}
// 工作线程启动过程WorkerImpl::WorkerImpl(ServerImpl& server, uint32_t index) : server_(server), dispatcher_(server_.api().allocateDispatcher( absl::StrCat("worker_", index))), connection_handler_(std::make_unique<ConnectionHandlerImpl>(*dispatcher_, server_.config().runtime(), server_.api())), scope_manager_(std::make_unique<ScopeManager>(server.stats())), thread_factory_(server.api().threadFactory()) {
// 工作线程初始化任务 dispatcher_->post([this]() { initializeThreadLocalData(); initializeListeners(); });
// 启动工作线程 thread_ = thread_factory_.createThread([this]() { run(); });}3.2 监听器管理
// 监听器分配到工作线程void ListenerImpl::addOrUpdate(Server::Instance& server) { // 为每个工作线程创建监听器实例 for (uint32_t i = 0; i < server.workerCount(); ++i) { auto& worker = server.worker(i); worker.dispatcher().post([this, &worker]() { // 在工作线程上创建监听器 worker.connectionHandler().addListener(*this); }); }}
// 工作线程上的监听器初始化void ConnectionHandlerImpl::addListener(Network::ListenerConfig& config) { // 创建主动 TCP 监听器 auto active_listener = std::make_unique<ActiveTcpListener>( *this, config, server_.runtime(), server_.stats());
// 开始监听 active_listener->listen();
// 存储监听器 active_tcp_listeners_.push_back(std::move(active_listener));}4. 任务调度和通信
4.1 任务调度架构
┌─────────────────────────────────────────────────────────────────┐│ 任务调度和通信架构 │└─────────────────────────────────────────────────────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐│ 主线程任务队列 │ │ 工作线程任务队列 │ │ 任务调度器 │└──────────┬───────┘ └──────────┬───────┘ └──────────┬───────┘ │ │ │ │ 1. 主线程提交任务 │ │ │───────────────────────▶│ │ │ │ │ │ │ 2. 任务调度器分配任务 │ │ │───────────────────────▶│ │ │ │ │ │ 3. 工作线程处理任务 │ │ │───────────────────────▶│ │ │ │ │ │ 4. 任务结果返回 │ │ │───────────────────────▶│ │ │ │ │ 5. 主线程处理结果 │ │ │◀──────────────────────┴──────────────────────┘4.2 线程通信实现
// 任务调度实现class TaskDispatcherImpl : public TaskDispatcher {public: // 将任务调度到特定工作线程 void postToWorker(uint32_t worker_index, TaskCb task) override { if (worker_index < workers_.size()) { workers_[worker_index].dispatcher().post(std::move(task)); } }
// 将任务调度到任意工作线程(负载均衡) void postToWorker(TaskCb task, bool force_event) override { if (workers_.size() == 1) { workers_[0].dispatcher().post(std::move(task)); return; }
// 轮询选择工作线程 uint32_t selected_index = next_worker_.fetch_add(1) % workers_.size(); workers_[selected_index].dispatcher().post(std::move(task)); }
// 将任务调度到主线程 void postToMain(TaskCb task) override { main_dispatcher_.post(std::move(task)); }
private: std::vector<WorkerImpl>& workers_; Event::Dispatcher& main_dispatcher_; std::atomic<uint32_t> next_worker_{0};};
// 任务处理过程void WorkerImpl::run() { // 事件循环 while (true) { // 处理事件和任务 dispatcher_->run(Event::Dispatcher::RunType::Block);
// 检查是否需要停止 if (should_stop_) { break; } }}5. 连接接受和处理
5.1 连接平衡和调度
┌─────────────────────────────────────────────────────────────────┐│ 连接平衡和调度 │└─────────────────────────────────────────────────────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐│ 监听器 │ │ 工作线程 │ │ 连接调度 │└──────────┬───────┘ └──────────┬───────┘ └──────────┬───────┘ │ │ │ │ 1. 监听网络地址 │ │ │───────────────────────▶│ │ │ │ │ │ │ 2. 接受新连接 │ │ │───────────────────────▶│ │ │ │ │ │ 3. 连接调度和平衡 │ │ │───────────────────────▶│ │ │ │ │ │ 4. 连接处理 │ │ │───────────────────────▶│ │ │ │ │ │ 5. 连接关闭和清理 │ │ │───────────────────────▶│ │ │ │ │ 6. 统计信息更新 │ │ │◀──────────────────────┴──────────────────────┘5.2 连接调度实现
// 连接调度到工作线程void ActiveTcpListener::onAcceptWorker(Network::ConnectionSocketPtr&& socket, bool hand_off_restored_destination_connections, bool rebalanced) { // 检查是否需要重新平衡连接 if (!rebalanced) { Network::BalancedConnectionHandler& target_handler = connection_balancer_.pickTargetHandler(*this); if (&target_handler != this) { target_handler.post(std::move(socket)); return; } }
// 创建 ActiveTcpSocket 实例并开始处理 auto active_socket = std::make_unique<ActiveTcpSocket>(*this, std::move(socket), hand_off_restored_destination_connections); onSocketAccepted(std::move(active_socket));}
// 连接平衡策略实现class ConnectionBalancerImpl : public Network::BalancedConnectionHandler {public: ConnectionBalancerImpl(std::vector<Network::BalancedConnectionHandler*>& handlers) : handlers_(handlers) {}
Network::BalancedConnectionHandler& pickTargetHandler( Network::BalancedConnectionHandler& current) override { // 轮询策略 static thread_local uint32_t index = 0; index = (index + 1) % handlers_.size(); return *handlers_[index]; }
private: std::vector<Network::BalancedConnectionHandler*>& handlers_;};6. 线程本地存储和资源管理
6.1 线程本地存储架构
┌─────────────────────────────────────────────────────────────────┐│ 线程本地存储架构 │└─────────────────────────────────────────────────────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐│ 全局资源 │ │ 线程本地存储 │ │ 资源访问 │└──────────┬───────┘ └──────────┬───────┘ └──────────┬───────┘ │ │ │ │ 1. 资源初始化 │ │ │───────────────────────▶│ │ │ │ │ │ │ 2. 线程本地副本创建 │ │ │───────────────────────▶│ │ │ │ │ │ 3. 资源访问和更新 │ │ │───────────────────────▶│ │ │ │ │ │ 4. 资源一致性维护 │ │ │───────────────────────▶│ │ │ │ │ 5. 资源销毁和清理 │ │ │◀──────────────────────┴──────────────────────┘6.2 线程本地存储实现// 线程本地存储实现template <typename T>class ThreadLocalImpl : public ThreadLocal<T> {public: // 获取当前线程的本地值 const T& get() override { const auto& current_thread_id = Thread::currentThreadId(); return getOrCreateLocalValue(current_thread_id); }
// 设置所有线程的本地值(主线程操作) void set(const T& value) override { // 首先更新主线程的本地值 getOrCreateLocalValue(Thread::mainThreadId()) = value;
// 发送更新到所有工作线程 for (auto& worker : workers_) { worker.dispatcher().post([this, value]() { const auto& thread_id = Thread::currentThreadId(); getOrCreateLocalValue(thread_id) = value; }); } }
private: // 获取或创建线程本地值 T& getOrCreateLocalValue(const Thread::Id& thread_id) { auto it = local_values_.find(thread_id); if (it == local_values_.end()) { auto emplace_result = local_values_.emplace(thread_id, value_factory_()); it = emplace_result.first; } return it->second; }
std::unordered_map<Thread::Id, T> local_values_; std::function<T()> value_factory_; std::vector<WorkerImpl>& workers_;};
// 线程本地资源访问void ClusterManagerImpl::initializeThreadLocalData() { // 为每个线程创建线程本地集群数据 thread_local_clusters_ = std::make_unique<ThreadLocalImpl<ThreadLocalCluster>>( [this]() { return std::make_unique<ThreadLocalCluster>(*this); });}7. 性能优化和最佳实践
7.1 工作线程配置
# Envoy 工作线程配置admin: address: socket_address: { address: 0.0.0.0, port_value: 9901 }
static_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 80 } filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: { prefix: "/" } route: { cluster: "local_cluster" } http_filters: - name: envoy.filters.http.router
clusters: - name: local_cluster connect_timeout: 0.25s type: STATIC lb_policy: ROUND_ROBIN load_assignment: cluster_name: local_cluster endpoints: - lb_endpoints: - endpoint: address: socket_address: { address: 127.0.0.1, port_value: 8080 }
# 线程配置(通过命令行或配置文件)# 命令行:--concurrency 4# 或配置文件中的配置:# concurrency: 47.2 监听器连接平衡
# 监听器连接平衡配置static_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 80 } connection_balance_config: consistent_hash_lb_config: use_source_ip: true filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: { prefix: "/" } route: { cluster: "local_cluster" } http_filters: - name: envoy.filters.http.router8. 故障排查和监控
8.1 线程相关指标
# 主线程指标envoy.server.version_info - Envoy 版本信息envoy.server.uptime - 服务器启动时间envoy.server.total_connections - 服务器接受的总连接数
# 工作线程指标envoy.server.worker_0.concurrency - 工作线程 0 处理的连接数envoy.server.worker_1.concurrency - 工作线程 1 处理的连接数...envoy.server.total_worker_concurrency - 所有工作线程处理的总连接数envoy.server.dispatcher.0.dispatcher.dispatch_time_ms - 调度器 0 的调度时间envoy.server.dispatcher.1.dispatcher.dispatch_time_ms - 调度器 1 的调度时间...8.2 调试技巧
# 启用详细的线程日志envoy --config-path envoy.yaml --log-level debug --component-log-level connection:debug,server:debug
# 检查线程配置curl -v http://localhost:9901/stats | grep envoy.server
# 检查工作线程调度curl -v http://localhost:9901/server_info
# 使用 pprof 分析线程性能curl -v http://localhost:9901/pprof/heap > heap.profcurl -v http://localhost:9901/pprof/profile?seconds=30 > profile.profgo tool pprof -http=:8080 profile.prof9. 总结
Envoy 的多线程架构设计使得它能够高效地处理大量并发连接。通过将连接生命周期绑定到单个工作线程,Envoy 避免了复杂的线程同步问题,并实现了令人尴尬的并行。
核心优势:
高性能:无阻塞设计和线程绑定的连接处理
可扩展性:通过增加工作线程数量轻松扩展
简单性:大部分代码是单线程的,简化了开发和维护
可靠性:线程本地存储和任务调度机制确保资源一致性
未来改进方向:
更智能的任务调度:根据工作负载动态调整线程分配
更细粒度的资源控制:支持基于连接类型的线程调度
性能优化:进一步减少线程通信开销
通过深入理解 Envoy 的多线程架构,我们可以更好地配置和优化 Envoy,以满足不同工作负载的性能需求。
827

被折叠的 条评论
为什么被折叠?



