2

2019-06-17 13:24 灵雀云 分类:istio 阅读(22) 评论(0) [编辑]
Envoy是istio的核心组件之一,以sidecar的方式与服务运行在一起,对服务的流量进行拦截转发。 具有路由,流量控制等等强大特性。

Envoy利用libevent实现了基于事件触发的异步架构,所有的网络阻塞操作包括 accept,read, connect, write 都是由eventloop进行callback触发。

本文以istio1.1所对应的Envoy版本进行源码流程分析。

名词解释:

下游: 发送请求给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分类

  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:
图片描述

  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功能。

灵雀云
167 声望38 粉丝

灵雀云Alauda成立于2014年,由原微软Azure云平台的核心创始团队创立。作为容器服务和企业级PaaS领域的领军企业,灵雀云的技术团队拥有全球领先、超大规模企业级云平台的开发、运维和管理经验,并在西雅图和北京都...