云原生小课堂 | Envoy 请求流程源码解析(二):请求解析
前言
Envoy 是一款面向 Service Mesh 的高性能网络代理服务。它与应用程序并行运行,通过以平台无关的方式提供通用功能来抽象网络。当基础架构中的所有服务流量都通过 Envoy 网格时,通过一致的可观测性,很容易地查看问题区域,调整整体性能。
Envoy 也是 istio 的核心组件之一,以 sidecar 的方式与服务运行在一起,对服务的流量进行拦截转发,具有路由,流量控制等等强大特性。本系列文章,我们将不局限于 istio,envoy 的官方文档,从源码级别切入,分享 Envoy 启动、流量劫持、http 请求处理流程的进阶应用实例,深度分析 Envoy 架构。
本篇是 Envoy 请求流程源码解析的第二篇,主要分享 Envoy 的 outbound 方向上篇,包含启动监听和建立连接。注:本文中所讨论的 issue 和 pr 基于 21 年 12 月。
envoy 当中基于 libevent 进行封装了各种文件,定时器事件等操作,以及 dispatch 对象的分发,和延迟析构,worker 启动,worker listener 绑定等部分不在这里作解读,后续有空可以单独再进行分析。跳过 envoy 当中的事件循环模型,这里以请求触发开始。
outbound 方向
filter 解析
启动监听
通过 xDS 或者静态配置,获得 Envoy 代理的监听器信息
如果监听器 bind_to_port,则直接调用 libevent 的接口,绑定监听,回调函数设置为 ListenerImpl::listenCallback
关于 reuseport
https://github.com/envoyproxy/envoy/issues/4602#issuecomment-544704931
https://github.com/envoyproxy/envoy/issues/8794
https://lwn.net/Articles/542629/
https://tech.flipkart.com/linux-tcp-so-reuseport-usage-and-implementation-6bfbf642885a
多个 server socket 监听相同的端口。每个 server socket 对应一个监听线程。内核 TCP 栈接收到客户端建立连接请求(SYN)时,按 TCP 4 元组(srcIP,srcPort,destIP,destPort) hash 算法,选择一个监听线程,唤醒之。新连接绑定到被唤醒的线程。所以相对于非 SO_REUSEPORT, 连接更为平均地分布到线程中(hash 算法不是绝对平均)
envoy 当中是支持在 listener 去设置开启这个特性,但是热重启场景时,对内核版本有一定要求(4.19-rc1)
https://www.envoyproxy.io/docs/envoy/v1.18.3/api-v3/config/listener/v3/listener.proto
验证观察
默认未开启,通过 envoyfilter 进行开启后,可见 15001 的端口被开启
需要重启 POD
而对于没有应用 reuseport
大致的平均
关于绝对的链接平衡, 可以试试 Listener 的配置 connection_balance_config:exact_balance,不过由于有锁,对高频新连接应该有一定的性能损耗。目前只适用于 TCP 监听器
建立连接
DispatcherImpl 通过 libevent,接收到请求,调用 ListenerImpl::listenCallback
client 向 envoy 发起连接,envoy 的 worker 接收 eventloop 的 callback, 触发 Envoy::Network::ListenerImpl::listenCallback(port: 15001)
15001 的
useOriginalDst": true
,accept_filters_
中会带有OriginalDstFilter
在
OriginalDstFilter.OnAccept
中用os_syscalls.getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, &orig_addr, &addr_len)
获取在 iptables 修改之前 dst ip iptables 与 getsockopt
在 newconnection 当中,还会通过 getBalancedHandlerByAddress 寻找到实际的虚拟 listener
通过 ConnectionHandlerImpl::findActiveListenerByTag
查到 addr 对应的 Listener
先查找 Listener.IP==addr.ip && Listener.Port==addr.port 的 Listener
再查找 Listener.IP==0.0.0.0 && Listener.Port==addr.port 的 Listener (对于 tcp 服务,ip 会有值,对于 http 服务,ip 为 4 个 0)
dispatcher.createServerConnection 传入 accept 到的 fd 创建 Server 连接对象 ConnectionImpl, 并把 onFileEvent 注册到 eventloop,等待读写事件的到来,因为 socket 是由一个 non-blocking listening socket 创建而来,所以也是 non-blocking
且注册的触发方式为 epoll 的边缘触发
http 的 listener 里 filters 为 envoy.http_connection_manager,buildFilterChain 里会把 HTTP::ConnectionManagerImpl 加入到 upstream_filters_(list)中,这样在请求数据到达的时候,就可以使用 http_connection_manager 的 on_read 方法
当连接刚刚加入 eventloop 的时候, Write Event 会被立即触发,但因为 write_buffer_没有数据,所以不会写入任何数据
ASM 试用申请
Envoy 是 Istio 中的 Sidecar 官方标配,是一个面向 Service Mesh 的高性能网络代理服务。当前 Service Mesh 是 Kubernetes 上微服务治理的最佳实践,灵雀云微服务治理平台 Alauda Service Mesh(简称:ASM)可完整覆盖微服务落地所需要的基础设施,让开发者真正聚焦业务。如果您想深入体验 ASM,扫描下方二维码即可报名!
关于【云原生小课堂】
【云原生小课堂】是由灵雀云、Kube-OVN 社区、云原生技术社区联合开设的公益性技术分享类专题,将以丰富详实的精品内容和灵活多样的呈现形式,持续为您分享云原生前沿技术,带您了解更多云原生实践干货。
在数字化转型的背景下,云原生已经成为企业创新发展的核心驱动力。作为国内最早将 Kubernetes 产品化的厂商之一,灵雀云从出生便携带“云原生基因”,致力于通过革命性的技术帮助企业完成数字化转型,我们期待着云原生给这个世界带来更多改变。
关注我们,学习更多云原生知识,一起让改变发生。
版权声明: 本文为 InfoQ 作者【York】的原创文章。
原文链接:【http://xie.infoq.cn/article/1e907972f8474e2dd1b16218a】。文章转载请联系作者。
评论