解析envoy處理http請求(上):filter架構

2019-06-17 13:24 靈雀雲 分類:istio 閱讀(22) 評論(0) [編輯]
Envoy是istio的核心組件之一,以sidecar的方式與服務運行在一塊兒,對服務的流量進行攔截轉發。 具備路由,流量控制等等強大特性。mysql

Envoy利用libevent實現了基於事件觸發的異步架構,全部的網絡阻塞操做包括 accept,read, connect, write 都是由eventloop進行callback觸發。c++

本文以istio1.1所對應的Envoy版本進行源碼流程分析。redis

名詞解釋:sql

下游: 發送請求給Envoy的服務,client
上游:接收Envoy發送的請求,並返回響應的服務, server
Filter流程圖
下面的流程圖爲istio架構下,訪問80端口的http服務的流程。
圖片描述網絡

  1. Client向Envoy的15001 port創建鏈接,被轉到80 port的Listener

2.Client發送請求給Envoy,Envoy通過路由後找到上游Server,併發送請求架構

圖片描述

3.上游Server返回響應給Envoy,Envoy利用event_active當即返回響應給下游的client併發

圖片描述

  1. Client主動斷開下游到Envoy的鏈接

圖片描述

  1. Server主動斷開Envoy到上游的鏈接

圖片描述

Filter分類cors

  1. ListenerFilter

listener.listener_filters負載均衡

用於接收到下游新鏈接的時候回調dom

接口:

onAccept(callback)
內置類型:

envoy.listener.original_dst (istio中的15001端口經常使用)
根據iptables轉換以前的dst port,查找到真實的Listener,查找到Listener會根據新的Listener的配置繼續處理

envoy.listener.tls_inspector
註冊read callback,識別tls和進行tls握手,握手結束後會進行下一步的filterChain的處

註冊filter:
圖片描述

  1. ReadFilter

listener.filter_chains.filters

用於接受到下游新鏈接的時候回調
上游或者下游鏈接上有數據能夠讀取的時候的回調,通常用於協議的解析
接口:

onNewConnection()
onData(data, end_stream)

內置類型:

Envoy::Http::CodecClient 只在向上遊的鏈接用到,且向上遊的鏈接只有這個filter,用於讀取響應
envoy.http_connection_manager
處理http請求的主要filter

envoy.tcp_proxy
envoy.redis_proxy

註冊filter:

圖片描述

  1. WriteFilter

listener.filter_chains.filters

用於向上遊的鏈接寫入數據的時候回調(目前內置的writeFilter沒有http相關的)

接口:

onWrite(data, end_stream)
內置類型:

envoy.filters.network.dubbo_proxy
envoy.mongo_proxy
envoy.filters.network.mysql_proxy
envoy.filters.network.zookeeper_proxy
註冊filter:

圖片描述

  1. StreamDecodeFilter ( envoy.http_connection_manager下獨有的filter)

listener.filter_chains.filters[envoy.http_connection_manager].http_filters

用於解析http請求各個部分的時候回調執行

接口:

decodeHeaders(headers, end_stream)
decodeData(data, end_stream)
decodeTrailers(HeaderMaps& trailers)
decodeComplete()

內置類型:

envoy.cors
envoy.fault
envoy.router

註冊filter:

圖片描述

  1. StreamEncodeFilter

(envoy.http_connection_manager 下獨有的filter)

listener.filter_chains.filters[envoy.http_connection_manager].http_filters

發送響應各個部分給下游client的時候執行

接口:

encode100ContinueHeaders(headers)
encodeHeaders(headers, end_stream)
encodeData(data, end_stream)
encodeTrailers(HeaderMap& trailers)
encodeMetadata(metadata_map)
encodeComplete()

內置類型:

envoy.cors
envoy.fault
envoy.lua

註冊filter:

圖片描述

  1. PerFilterConfig (並非filter,只是爲4,5中的http_filter提供route級別的配置數據)

route.virtual_hosts.per_filter_config

位於route上的字段,只有當對應Listener上http_connection_manager包含對應httpfilter的時候纔有用,結構爲 map<string, Struct> 用法由filter本身實現

7.ConnectionCallbacks

listener.filter_chains.filters

接口:

onEvent(event)
事件分爲 RemoteClose, LocalClose, Connected 會在各個階段調用

onAboveWriteBufferHighWatermark()
onBelowWriteBufferLowWatermark()
route.virtual_hosts.per_filter_config
類型:

Envoy::Http::CodeClient只在向上遊的鏈接用到,且向上遊的鏈接只有這個filter,用於檢測上游鏈接斷開
envoy.http_connection_manager
envoy.tcp_proxy
envoy.redis_proxy
註冊filter:

圖片描述

  1. access_log_handlers

接口:

log(request_headers, response_headers, response_trailers, stream_info)
類型:

Envoy::Http::Mixer::Filter
istio爲Envoy添加的Filter,在AccessLogHandlers這邊主要用於Report
Envoy::Extensions::AccessLoggers::File::FileAccessLog
Envoy::Extensions::HttpGrpc::File::HttpGrpcAccessLog
Envoy::Extensions::HttpFilters::TapFilter::Filter
Filter流程中關鍵步驟解析

  1. findActiveListenerByAddress

根據socket的localaddress和port選擇合適的Listener處理

1.利用syscall找到iptables轉化以前的dst port (若是有envoy.listener.original_dst)
os_syscalls.getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, &orig_addr, &addr_len)

2.先匹配address和port和socket都一致的Listener,若是沒找到再找port一致,address==0.0.0.0的Listener

圖片描述

  1. 匹配request選擇route和cluster

在構造RouteMatcher的時候會遍歷virtual_hosts 下的domains,並根據通配符的位置和domain的長度分爲4個 map<domain_len, std::unordered_map<domain, virtualHost>, std::greater<int64_t>>

default_virtual_host_ domain就是一個通配符(只容許存在一個)
wildcard_virtual_host_suffixes_ domain中通配符在開頭
wildcard_virtual_host_prefixes_ domain中通配符在結尾
virtual_hosts_ 不包含通配符

圖片描述

  1. 按照 virtual_hosts_ => wildcard_virtual_host_suffixes_ => wildcard_virtual_host_prefixes_ => default_virtual_host_ 的順序查找

同時按照map的迭代順序(domain len降序)查找最早除去通配符後能匹配到的virtualhost,若是沒有直接返回 404

3.在一個virtualhost上查找對應route和cluster

在經過domain匹配到virtualhost,會在那個virtualhost上匹配查找cluster,若是沒匹配上,會直接返回404

圖片描述
match能夠根據配置分爲 prefix, regex, path 三種route進行匹配
若是存在weighted_clusters ,會根據stream_id , 和clusters的weight進行分發,stream_id 自己是每一個請求獨立隨機生成,因此weighted_clusters 的權重分發能夠視爲隨機分發
3.負載均衡策略選擇endpoint
1.在上一步查找到了clusterName, 對於clusterEntry,都是從ThreadLocalClusterManagerImpl 中取出,每一個worker都一份本身的數據

2.對於ThreadLocalClusterManagerImpl , 維護了多份根據類型和協議區分的map

其中http協議才用的是host_http_conn_pool_map_ 這個map,大體的結構爲 map<host, map<protocol, connpool>> , 由於http分爲 Http10, Http11, Http2 不一樣協議的connpool都是獨立的

對於http請求,會從 host_http_conn_pool_map_ 中查到對應的connpool,每一個worker都維護了一份本身獨有的threadlocal connpool

Mixer
mixerclient是istio基於Envoy,添加filter進行check和report的模塊

註冊到Envoy

圖片描述

註冊到Envoy主要就是兩行

圖片描述
第一行註冊了 StreamDecodeFilter 和 StreamEncodeFilter, Http::Mixer::Filter 在decodeHeader 這個hook中實現了Check,發送attributes給mixerserver進行檢查

第二行註冊了 AccessLogHandler ,這個會在 一個請求結束的時候執行

圖片描述

在Mixer filter的log method中,會進行report操做

能夠看到Mixer雖然是每一個請求結束都會調用log,但實際的上報mixer是批量發送(累計必定大小或者到達必定時間間隔)。

總結
1.能夠在Envoy處理請求的各個階段加入filter來定製化功能,能夠本身編寫c++的filter,用REGISTER_FACTORY 註冊到對應的Factory map中。

  1. istio經過mixer filter實現了check和report功能。
相關文章
相關標籤/搜索