在线上环境运行使用envoy时,假如服务是短连接,我们发现envoy每个core使用率基本是均衡的,但是当有grpc等长连接时,我们会发现某些core使用率已经100%,但是其他的core却处于空闲状态,这样就会导致下游服务访问超时,本文将分析解决该问题
线程模型
Envoy使用具有多个线程体系结构的单个进程。单个主线程控制各种零星的协调任务,同时一些工作线程执行侦听,筛选和转发。listener接受连接后,该连接将在其整个生命周期的余下时间内绑定到单个工作线程。这使大多数Envoy基本上都是单线程的(令人尴尬的是并行的),并在工作线程之间进行了少量更复杂的代码处理协调。通常,Envoy被编写为100%无阻塞,对于大多数工作负载,我们建议将工作线程的数量配置为等于计算机上的硬件线程的数量。
由此可以看到,当长连接建立连接后,是不会在envoy的多个worker之间切换的,而
默认情况下,工作线程之间没有协调。这意味着所有工作线程独立地尝试接受每个listener上的连接,并依赖内核在线程之间执行适当的平衡。对于大多数工作负载,内核在平衡传入连接方面做得非常好。但是,对于某些工作负载,尤其是那些具有少量非常长的连接(例如,服务网格HTTP2/gRPC出口)的工作负载,可能需要让Envoy强制平衡工作线程之间的连接。为了支持此行为,Envoy允许在每个侦听器上配置不同类型的连接平衡。
由此可以看到,当长连接建立连接后,是不会在envoy的多个worker之间切换的,当建立大量连接时,随机分配连接到每个worker上是没有问题的,但是在长连接情况下,因为连接很长时间才会中断,当多个长连接都绑定到一个worker上后,就会造成该core使用率过高
连接平衡
connection_balance_config
监听器的连接平衡器配置,目前只适用于TCP监听器。如果未指定配置,则Envoy不会尝试平衡辅助线程之间的活动连接。
在listener X通过将X中的use_original_dst和Y1和Y2中的bind_to_port设置为false来将所有连接重定向到listener Y1和Y2的情况下,建议禁用listener X中的balance配置以避免平衡的开销,并且在Y1和Y2中启用balance配置,以平衡工作进程之间的连接。
exact_balance
如果指定,则侦听器将使用准确的连接平衡器。
代码语言:javascript复制 "connection_balance_config": { "exact_balance": {} }
当配置了exact_balance连接平衡策略,可以让连接在worker之间重新平衡,在执行平衡策略期间,连接将保持锁定,这种平衡策略为了保持准确性而牺牲了吞吐量,所以适合在新建连接很少的情况下使用
istio中使用exact_balance
istio原生并没有支持exact_balance,但是我们可以通过EnvoyFilter来进行配置
代码语言:javascript复制kubectl -n hobby apply -f - <<EOFapiVersion: networking.istio.io/v1alpha3kind: EnvoyFiltermetadata: name: productpage-listener-balance namespace: hobbyspec: workloadSelector: labels: app: productpage configPatches: - applyTo: LISTENER match: context: SIDECAR_OUTBOUND listener: address: 0.0.0.0 portNumber: 9080 patch: operation: MERGE value: connection_balance_config: exact_balance: {}EOF
但是由于envoy只有一个默认的真实监听的端口其他都是不绑定端口的,相当于listenerA 转发到listener B,而扩展平衡机制需要应用到真正工作的listener才是有意义的,在之前版本在转发场景只有listenerA的平衡配置才能生效,在新的版本解决了这个问题,详细可以看pr[1]
引用链接
[1]
pr: https://github.com/envoyproxy/envoy/pull/15842