事件驱动#
图: Event Loop of Envoy#
不出意外,Envoy 使用了 libevent 这个 C 事件 library, libevent 使用了 Linux Kernel 的 epoll 事件驱动 API。
说明一下图中的流程:
Envoy worker 线程挂起在
epoll_wait()方法中,在内核中注册等待 epoll 关注的 socket 发生事件。线程被移出 kernel 的 runnable queue。线程睡眠。内核收到 TCP 网络包,触发事件
操作系统把 Envoy worker 线程移入 kernel 的 runnable queue。Envoy worker 线程被唤醒,变成 runnable。操作系统发现可用 cpu 资源,把 runnable 的 envoy worker 线程调度上 cpu。(注意,runnable 和 调度上 cpu 不是一次完成的)
Envoy 分析事件列表,按事件列表的 fd 调度到不同的
FileEventImpl类的回调函数(实现见:FileEventImpl::assignEvents)FileEventImpl类的回调函数调用实际的业务回调函数执行 Envoy 的实际代理行为
完事后,回到步骤 1 。
HTTP 反向代理的总流程#
整体看,Socket 事件驱动的 HTTP 反向代理总流程如下:
图中看出,有 5 种事件驱动了整个流程。后面几节会逐个分析。
Downstream TCP 连接建立#
现在看看,事件驱动和连接的建立的过程和关系:
Envoy worker 线程挂起在
epoll_wait()方法中。线程被移出 kernel 的 runnable queue。线程睡眠。client 建立连接,server 内核完成3次握手,触发 listen socket 事件。
操作系统把 Envoy worker 线程移入 kernel 的 runnable queue。Envoy worker 线程被唤醒,变成 runnable。操作系统发现可用 cpu 资源,把 runnable 的 envoy worker 线程调度上 cpu。(注意,runnable 和 调度上 cpu 不是一次完成的)
Envoy 分析事件列表,按事件列表的 fd 调度到不同的
FileEventImpl类的回调函数(实现见:FileEventImpl::assignEvents)FileEventImpl 类的回调函数调用实际的业务回调函数,进行 syscall
accept,完成 socket 连接。得到新 socket 的 FD:$new_socket_fd。业务回调函数把 调用
epoll_ctl把$new_socket_fd加到 epoll 监听中。回到步骤 1 。