首先描述Envoy如何適合請求的請求路徑,而後描述內部請求從下游到達Envoy代理以後發生的內部事件c++
Envoy在其代碼庫和文檔中使用如下術語:算法
IP /端口
, 能夠被下游客戶端鏈接。Envoy 暴露一個或者多個監聽器給下游主機鏈接。請求如何流經網絡(包括Envoy)中的組件取決於網絡的拓撲。Envoy可用於多種網絡拓撲中。數據庫
Envoy最初是做爲Service Mesh Sidecar代理,從應用程序中排除了負載平衡,路由,可觀察性,安全性和發現服務。在Service Mesh模型中,請求數據流經Envoy做爲網絡的網關。請求經過入口或出口偵聽器到達Envoy:api
Ingress listeners
從Service Mesh中的其餘節點獲取請求,並將其轉發到本地應用程序。本地應用程序的響應經過Envoy流回到下游。安全
Egress listeners
從本地應用程序獲取請求,並將其轉發到網絡中的其餘節點。這些接收節點一般還將運行Envoy,並經過其入口偵聽器接受請求。服務器
除Service mesh外,Envoy還用於各類配置中。例如,它還能夠充當內部負載平衡器:網絡
做爲網絡邊緣上的入口/出口(ingress/egress proxy)代理:架構
在實際應用中,會混合使用上面的方法, 其中Envoy在service mesh(服務網格)中, 在邊緣網絡上做爲 內部負載均衡 使用。 請求路徑可能會遍歷多個Envoy併發
Envoy能夠在多層拓撲中進行配置以實現可伸縮性和可靠性,其中請求首先經過邊緣Envoy,而後再經過第二個Envoy層:負載均衡
在上述全部的網絡拓撲模型中,請求將從下游經過TCP
,UDP
或 Unix域套接字
到達特定的Envoy。 Envoy將經過TCP
,UDP
或Unix域套接字
向上遊轉發請求。
下面重點介紹一個Envoy代理模式
Envoy是擴展性很強的平臺。 這致使可能的請求路徑組合不少,具體取決於:
如下示例主要使用到以下配置相關
HTTP鏈接管理器
做爲惟一的網絡過濾器
。爲了簡單起見,假定使用靜態引導程序配置文件:example.yml
# 靜態資源 static_resources: listeners: # 簡單的監聽器,綁定端口443 - name: listener_https address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 443 # A single listener filter exists for TLS inspector. listener_filters: - name: "envoy.filters.listener.tls_inspector" typed_config: {} # On the listener, there is a single filter chain that matches SNI for acme.com. filter_chains: - filter_chain_match: # This will match the SNI extracted by the TLS Inspector filter. server_names: ["acme.com"] # 下游 TLS 配置. transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext common_tls_context: tls_certificates: - certificate_chain: { filename: "certs/servercert.pem" } private_key: { filename: "certs/serverkey.pem" } filters: # HTTP鏈接管理器 - 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 use_remote_address: true http2_protocol_options: max_concurrent_streams: 100 # 基於系統文件的 日誌 access_log: - name: envoy.access_loggers.file typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog path: "/var/log/envoy/access.log" # 路由表,匹配本地服務的 /foo請求 route_config: name: local_route virtual_hosts: - name: local_service domains: ["acme.com"] routes: - match: path: "/foo" route: cluster: some_service # CustomFilter and the HTTP router filter are the HTTP filter chain. http_filters: # - name: some.customer.filter - name: envoy.filters.http.router clusters: - name: some_service connect_timeout: 5s # 上游 TLS 認證. transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext load_assignment: cluster_name: some_service # 靜態端點分配(邏輯主機節點) endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 10.1.2.10 port_value: 10002 - endpoint: address: socket_address: address: 10.1.2.11 port_value: 10002 typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: max_concurrent_streams: 100 - name: some_statsd_sink connect_timeout: 5s # The rest of the configuration for statsd sink cluster. # statsd sink. stats_sinks: - name: envoy.stat_sinks.statsd typed_config: "@type": type.googleapis.com/envoy.config.metrics.v3.StatsdSink tcp_cluster_name: some_statsd_sink
Envoy中的請求處理路徑包括兩個主要部分:
這兩個子系統與HTTP路由器過濾器橋接,該過濾器將HTTP請求從下游轉發到上游。
Envoy還具備基於事件的線程模型。主線程負責服務器的生命週期,配置處理,狀態等,而且一些工做線程負責處理請求。全部線程都圍繞事件循環運行,當監聽器接收到一個鏈接請求時,該鏈接將其生命週期綁定到一個單獨的 worker 線程。worker 線程則負責執行監聽、過濾和轉發。每一個工做線程都維護本身的與上游端點的TCP鏈接池。UDP過濾器狀態爲給定的工做線程共享,過濾器負責根據須要提供會話語義。
worker線程不多共享狀態,而且不多並行運行。該線程模型能夠擴展到核心數量很是多的CPU。
使用上面的示例配置簡要概述了請求和響應的生命週期:
如圖
ListenerManager負責獲取偵聽器的配置,並實例化綁定到其各自IP /端口的多個偵聽器實例上。偵聽器可能處於如下三種狀態之一:
每一個工做線程爲每一個配置的偵聽器維護本身的偵聽器實例。每一個偵聽器均可以經過SO_REUSEPORT綁定到同一端口,或者共享一個綁定到該端口的套接字。當新的TCP鏈接到達時,內核將決定哪一個工做線程將接受該鏈接,而且該工做線程的偵聽器將調用其Server :: ConnectionHandlerImpl :: ActiveTcpListener :: onAccept()回調。
而後,工做線程的偵聽器將建立並運行偵聽器過濾器鏈。過濾器鏈是經過應用每一個過濾器的過濾器工廠而建立的。工廠知道過濾器的配置,併爲每一個鏈接或流建立一個新的過濾器實例。
Envoy經過TransportSocket擴展接口提供可插拔的運輸插座。傳輸套接字遵循TCP鏈接的生命週期事件,並讀寫網絡緩衝區。運輸套接字必須實現的一些關鍵方法是:
virtual void onConnected() PURE; virtual IoResult doRead(Buffer::Instance& buffer) PURE; virtual IoResult doWrite(Buffer::Instance& buffer, bool end_stream) PURE; virtual void closeSocket(Network::ConnectionEvent event) PURE;
當TCP鏈接上有可用數據時,Network :: ConnectionImpl :: onReadReady()會經過SslSocket :: doRead()調用TLS傳輸套接字。而後,傳輸套接字在TCP鏈接上執行TLS握手。握手完成後,SslSocket :: doRead()將解密的字節流提供給Network :: FilterManagerImpl的實例,該實例負責管理網絡過濾器鏈。
須要特別注意的是,不管是TLS握手仍是過濾器管道暫停,都沒法阻止任何操做。因爲Envoy是基於事件的,所以任何須要額外數據處理的狀況都將致使事件的儘早完成,並使CPU產生另外一個事件。當網絡使更多數據可供讀取時,讀取事件將觸發TLS握手的恢復。
與偵聽器過濾器鏈同樣,Envoy將經過Network :: FilterManagerImpl實例化其過濾器工廠中的一系列網絡過濾器。該實例對於每一個新鏈接都是新鮮的。網絡過濾器(如傳輸套接字)跟隨TCP生命週期事件,並隨着數據從傳輸套接字可用而被調用。
網絡過濾器是做爲管道組成的,與每一個鏈接一個的傳輸套接字不一樣。網絡過濾器分爲三種:
Envoy中的HTTP / 2編解碼器基於nghttp2。
對於每一個HTTP流,HCM都按照上面爲偵聽器和網絡過濾器鏈創建的模式實例化HTTP過濾器鏈。
HTTP過濾器接口共有三種:
請求路徑將以下所示:
響應路徑以下所示:
調用路由器過濾器時,路由將最終肯定。所選路由的配置將指向上游羣集名稱。而後,路由器過濾器向ClusterManager詢問羣集的HTTP鏈接池。
負載平衡是一種在單個上游羣集內的多個主機之間分配流量以有效利用可用資源的方法。有許多不一樣的方法能夠完成此任務,所以Envoy提供了幾種不一樣的負載平衡策略。從高層次上講,咱們能夠將這些策略分爲兩類:全局負載平衡和分佈式負載平衡。
分佈式負載平衡是指 讓Envoy本身基於瞭解上游主機的位置來肯定應如何將負載分配到端點。
例子:
- Active health checking: 經過對上游主機進行情況檢查,Envoy能夠調整優先級和位置的權重以解決不可用主機的問題。
- Zone aware routing: 可以使Envoy選擇使用更近的端點,而沒必要在控制平面中顯式配置優先級。
- Load balancing algorithms:Envoy可使用幾種不一樣的算法來使用提供的權重來肯定要選擇的主機
每一個集羣都有一個負載均衡器,當新請求到達時,該負載均衡器會選擇一個端點。Envoy支持多種負載平衡算法,例如加權輪循,磁懸浮列車,負荷最小,隨機。
負載平衡器從靜態引導程序配置,DNS,動態xDS(CDS和EDS發現服務)以及主動/被動運行情況檢查的組合中得到有效分配。
選擇端點後,將使用該端點的鏈接池來查找用於轉發請求的鏈接。若是不存在與主機的鏈接,或者全部鏈接均處於其最大併發流限制,則除非鏈接集羣最大鏈接的斷路器跳閘,不然將創建新鏈接並將其放置在鏈接池中。若是配置並達到了鏈接的最大生存期流限制,則會在池中分配一個新的鏈接,而且耗盡受影響的HTTP / 2鏈接。
所選鏈接的HTTP / 2編解器將請求流與單個TCP鏈接流向同一上游的任何其餘流進行多路複用。這與HTTP / 2編解碼器解碼相反。
與下游HTTP / 2解碼器同樣,上游編解碼器負責獲取Envoy對HTTP的標準抽象,即在單個鏈接上與請求/響應標頭/正文/尾部複用的多個流,並經過生成一系列HTTP / 2幀將其映射到HTTP / 2
上游端點鏈接的TLS傳輸套接字對HTTP / 2編解碼器輸出中的字節進行加密,並將其寫入用於上游鏈接的TCP套接字。與TLS傳輸套接字解密同樣,示例中,羣集配置了提供TLS傳輸安全性的傳輸套接字。上游和下游傳輸套接字擴展存在相同的接口。
Reuest 在上游被代理,Response在下游被代理。響應以相反的順序經過HTTP和網絡過濾器。從請求。
解碼器/編碼器請求生命週期事件的各類回調將在HTTP過濾器中調用,例如當響應預告片被轉發或請求主體被流式傳輸時。一樣,當請求期間數據繼續在兩個方向上流動時,讀/寫網絡過濾器也將調用其各自的回調。
請求有可能提早終止。這多是因爲(但不限於):
若是發生這些狀況中的任何一個,Envoy內部可能會發送生成的響應(若是還沒有發送上游響應頭),或者將流重置(若是響應頭已經轉發至下游)。
請求完成後,數據流將被銷燬。還會發生如下狀況: