Envoy是istio的核心组件之一,以sidecar的方式与服务运行在一起,对服务的流量进行拦截转发。 具有路由,流量控制等等强大特性。
Envoy利用libevent实现了基于事件触发的异步架构,所有的网络阻塞操作包括 accept,read, connect, write 都是由eventloop进行callback触发。
本文以istio1.1所对应的Envoy版本进行源码流程分析。
名词解释:
- 下游: 发送请求给Envoy的服务,client
- 上游:接收Envoy发送的请求,并返回响应的服务, server
下面的流程图为istio架构下,访问80端口的http服务的流程。
1. Client向Envoy的15001 port建立连接,被转到80 port的Listener
2.Client发送请求给Envoy,Envoy经过路由后找到上游Server,并发送请求
3.上游Server返回响应给Envoy,Envoy利用event_active立即返回响应给下游的client
4. Client主动断开下游到Envoy的连接
5. Server主动断开Envoy到上游的连接
Filter分类
1. ListenerFilter
listener.listener_filters
用于接收到下游新连接的时候回调
接口:
onAccept(callback)
内置类型:
- envoy.listener.original_dst (istio中的15001端口常用)
根据iptables转换之前的dst port,查找到真实的Listener,查找到Listener会根据新的Listener的配置继续处理
- envoy.listener.tls_inspector
注册read callback,识别tls和进行tls握手,握手结束后会进行下一步的filterChain的处
注册filter:
2. 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:
3. 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:
4. 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:
5. 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:
6. 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:
8. 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
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
2. 匹配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_ 不包含通配符
2. 按照 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
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中。
2. istio通过mixer filter实现了check和report功能。
登录后评论
立即登录 注册