本文基于 envoy 1.21.0 (latest)
1 过滤器介绍
从 socket 中收取的请求先经过 listener_filters 处理,然后再由 filter_chains 处理,前者包含的 filter 称为 listener filter,后者包含的 filter 称为 network filter。因为 listener_filters 先起作用,因此它可以修改请求的信息,从而影响 filter_chains 的匹配。
1.1 过滤器简述
-
监听器过滤器
可以在加入 listener 的 listener_filter 字段中的 listener filter。 这些 filter 的主要作用是检测协议、解析协议,通过它们解析出的信息被用于匹配 filter_chains 中的 filter。
例如:
envoy.listener.http_inspector:判断应用层的数据是否使用 HTTP 协议,如果是,续继判断 HTTP 协议的版本号(HTTP 1.0、HTTP 1.1、HTTP 2)。
envoy.listener.original_src:用于透明代理,让 uptream 看到的是请求端的 IP,双方均感知不到 envoy 的存在。
envoy.listener.original_dst: 用来读取 socket 的配置项 SO_ORIGINAL_DST,用该 filter 获取报文的原始目地地址。
envoy.listener.proxy_protocol:解析代理协议,用该 filter 可以解析出真实的源 IP。
envoy.listener.tls_inspector: 用来判断是否使用 TLS 协议,如果是 TLS 协议,解析出 Server Name、Negotiation 信息,解析出来的信息用于 FilterChain 的匹配。 -
network 过滤器
操作原始字节。允许混合不同的过滤器组合,并匹配和附加到给定的监听器。
网络过滤器在一个有序的列表中被链起来(过滤器栈),称为过滤器链。每个监听器都有多个过滤器链和一个可选默认过滤器链。如果找不到最佳匹配的过滤链,将选择默认的过滤链来处理请求。如果没有提供默认的过滤链,连接将被关闭。
参考:https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/network_filters/network_filters
例如:
envoy.filters.network.mysql_proxy:能够解析 mysql 的通信协议,需要和 [TCP proxy][] 一起使用。
配置文件
filter_chains:
- filters:
- name: envoy.filters.network.mysql_proxy
typed_config:
“@type”: type.googleapis.com/envoy.config.filter.network.mysql_proxy.v1alpha1.MySQLProxy
stat_prefix: mysql - name: envoy.tcp_proxy
typed_config:
“@type”: type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy
stat_prefix: tcp
cluster: …
- name: envoy.filters.network.mysql_proxy
envoy.http_connection_manager:专门处理 http 协议的 network filter,内部又实现了 http filter。因为 http 最经常使用的协议,对代理功能需求也非常多样, HTTP connection manager 本身是一个比较复杂的 network filter,在 envoy 文档中被单独列出:HTTP connection 。 proto message 字段参考 连接管理器.proto 。
- http 过滤器
在连接管理器中支持HTTP级别的过滤器栈。
envoy.filters.http.router :路由器过滤器,执行高级路由任务。参考
HTTP 转发是用路由过滤器实现的。该过滤器的主要职能就是执行路由表中的指令。在重定向和转发这两个主要任务之外,路由过滤器还需要处理重试、统计之类的任务。
1.2 network filter chain 配置
说明:envoy 中每一个资源对象的参数配置都是通过 protobuf 来定义的。
监听器配置
过滤链配置定义,这里指 网络过滤器链,
listener.FilterChain
{
“filter_chain_match”: “{…}”,
“tls_context”: “{…}”,
“filters”: [],
“use_proxy_proto”: “{…}”,
“transport_socket”: “{…}”
}
过滤器链包裹着一组匹配判据,一个选项TLS上下文,一组过滤器,以及其他各种参数。
具体字段的说明:
FilterChainMatch 配置定义
指定为 Listener 选择特定过滤链的匹配判据。
为了选择一个过滤链,它的所有判据必须被传入的连接满足,其属性由网络堆栈和/或监听器过滤器设置。
以下顺序适用:
- 目的地端口。
- 目的地IP地址。
- 服务器名称(例如:TLS协议的SNI)。
- 传输协议。
- 应用协议(例如:TLS协议的ALPN)。
- 直接连接的源IP地址(这只有在使用覆盖源地址的监听器过滤器时才会与源IP地址不同,如代理协议监听器过滤器)。
- 源类型(例如,任何,本地或外部网络)。
- 源IP地址。
- 源端口
proto message中各个字段说明
字段
格式
说明
destination_port
google.protobuf.UInt32Value
当监听器上设置use_original_dst时,在确定过滤链匹配时要考虑的可选目标端口。
prefix_ranges
core.v3.CidrRange[]
如果非空,则是一个IP地址和前缀长度,以便在监听器被绑定到0.0.0.0/::或指定use_original_dst时匹配地址。
direct_source_prefix_ranges
core.v3.CidrRange[]
如果下游连接的直接连接源IP地址包含在至少一个指定的子网中,则满足该条件。如果没有指定参数或者列表为空,直接连接的源IP地址将被忽略。
source_type
ConnectionSourceType
指定连接源IP匹配类型。可以是任何,本地或外部网络。
source_prefix_ranges
core.v3.CidrRange[]
如果下游连接的源IP地址包含在至少一个指定的子网中,则满足该条件。如果没有指定参数或列表为空,则源IP地址被忽略。
source_ports
uint32[]
如果下游连接的源端口包含在至少一个指定的端口中,则满足该条件。如果没有指定该参数,源端口将被忽略。
server_names
string[]
如果非空,则是一个服务器名称的列表(例如TLS协议的SNI),在确定过滤器链匹配时要考虑。这些值将与新连接的服务器名称进行比较,当被一个监听器过滤器检测到时。
服务器名称将与所有通配符域名进行匹配,即www.example.com,然后是*.example.com,然后是*.com。
注意,不支持部分通配符,像*w.example.com这样的值是无效的。
transport_protocol
string
如果非空,在确定过滤器链匹配时要考虑的传输协议。这个值将与新连接的传输协议进行比较,当它被一个监听器过滤器检测到时。
建议的值包括:
-
raw_buffer - 默认值,在没有检测到传输协议时使用。
-
tls - 当检测到TLS协议时由envoy.filters.listener.tls_inspector设置。
application_protocols
string[]
如果非空,则是一个应用协议的列表(例如,TLS协议的ALPN),在确定过滤器链的匹配时要考虑。这些值将与新连接的应用协议进行比较,当被一个监听器过滤器检测到时。
建议的值包括。
-
http/1.1 - 由 envoy. filters.listener.tls_inspector 设置。
-
h2 - 由 envoy.filters.listener.tls_inspector 设置。
- listener 初始化 和启动 源码概览
2.1 初始化中 解析配置文件的 listeners
核心函数:InstanceImpl::initialize
- 调用 MainImpl::initialize
初始化listeners配置
// source/server/server.cc envoy 初始化
void InstanceImpl::initialize(Network::Address::InstanceConstSharedPtr local_address,
ComponentFactory& component_factory) {
//…
config_.initialize(bootstrap_, *this, *cluster_manager_factory_);
//…
}
// source/server/configuration_impl.cc 解析 yaml配置文件,初始化 listeners
void MainImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bootstrap,
Instance& server,
Upstream::ClusterManagerFactory& cluster_manager_factory) {
//…
const auto& listeners = bootstrap.static_resources().listeners();
ENVOY_LOG(info, “loading {} listener(s)”, listeners.size());
for (ssize_t i = 0; i < listeners.size(); i++) {
ENVOY_LOG(debug, “listener #{}:”, i);
server.listenerManager().addOrUpdateListener(listeners[i], “”, false);
}
//…
}
// source/server/listener_manager_impl.cc 创建 ListenerImpl,ListenerImpl 做 bind 操作
bool ListenerManagerImpl::addOrUpdateListener(const envoy::config::listener::v3::Listener& config,
const std::string& version_info, bool added_via_api) {
//…
return addOrUpdateListenerInternal(config, version_info, added_via_api, name);
//…
}
bool ListenerManagerImpl::addOrUpdateListenerInternal(
const envoy::config::listener::v3::Listener& config, const std::string& version_info,
bool added_via_api, const std::string& name) {
//…
ListenerImplPtr new_listener = nullptr;
//…
ENVOY_LOG(debug, “use full listener update path for listener name={} hash={}”, name, hash);
new_listener =
std::make_unique(config, version_info, *this, name, added_via_api,
workers_started_, hash, server_.options().concurrency());
//…
}
主要目的:解析配置文件中的 listeners , 以构造对应的 ListenerImpl 。
- 配置文件举例
envoy配置文件
static_resources:
listeners:- address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
“@type”: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: AUTO
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: backend
domains:
- “*”
routes:
- match:
prefix: “/service/1”
route:
cluster: service1
- match:
prefix: “/service/2”
route:
cluster: service2
http_filters:- name: envoy.filters.http.router
- name: envoy.filters.network.http_connection_manager
- filters:
- address:
- proto 定义message(.proto文件 → .pb.h 和 .pb.cc文件, 将 message 转成 class)
proto中的message定义
message Bootstrap {
message StaticResources {
repeated listener.v3.Listener listeners = 1;
repeated cluster.v3.Cluster clusters = 2;
repeated envoy.extensions.transport_sockets.tls.v3.Secret secrets = 3;
}
StaticResources static_resources = 2;
// …
}
message Listener {
string name = 1;
core.v3.Address address = 2 [(validate.rules).message = {required: true}];
repeated FilterChain filter_chains = 3;
FilterChain default_filter_chain = 25;
repeated ListenerFilter listener_filters = 9;
// …
}
参考 https://cloudnative.to/envoy/api-v3/config/listener/v3/listener.proto.html
各种过滤器:https://cloudnative.to/envoy/api-v3/config/filter/filter.html#
HttpConnectionManager参考https://cloudnative.to/envoy/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto.html
- ListenerImpl 构造函数
(1)调用 createListenerFilterFactories 来获取 listener_filters ,赋值给 其成员变量 listener_filter_factories_
解析 listener_fileters 配置
void ListenerImpl::createListenerFilterFactories(Network::Socket::Type socket_type) {
if (!config_.listener_filters().empty()) {
switch (socket_type) {
case Network::Socket::Type::Datagram:
udp_listener_filter_factories_ = parent_.factory_.createUdpListenerFilterFactoryList(
config_.listener_filters(), *listener_factory_context_);
break;
case Network::Socket::Type::Stream:
// 注意下面这一行, 这里的 config_ 是 const envoy::config::listener::v3::Listener
listener_filter_factories_ = parent_.factory_.createListenerFilterFactoryList(
config_.listener_filters(), *listener_factory_context_);
break;
default:
NOT_REACHED_GCOVR_EXCL_LINE;
}
}
}
// createListenerFilterFactoryList 调用下面这个函数
std::vectorNetwork::ListenerFilterFactoryCb
ProdListenerComponentFactory::createListenerFilterFactoryList_(
const Protobuf::RepeatedPtrFieldenvoy::config::listener::v3::ListenerFilter& filters,
Configuration::ListenerFactoryContext& context) {
std::vectorNetwork::ListenerFilterFactoryCb ret;
for (ssize_t i = 0; i < filters.size(); i++) {
const auto& proto_config = filters[i];
ENVOY_LOG(debug, " filter #{}:“, i);
ENVOY_LOG(debug, " name: {}”, proto_config.name());
ENVOY_LOG(debug, " config: {}",
MessageUtil::getJsonStringFromMessageOrError(
static_cast<const Protobuf::Message&>(proto_config.typed_config())));
// Now see if there is a factory that will accept the config.
auto& factory =
Config::Utility::getAndCheckFactoryConfiguration::NamedListenerFilterConfigFactory(
proto_config);
auto message = Config::Utility::translateToFactoryConfig(
proto_config, context.messageValidationVisitor(), factory);
ret.push_back(factory.createListenerFilterFactoryFromProto(
*message, createListenerFilterMatcher(proto_config), context));
}
return ret;
}
(2)调用了buildFilterChains 来获取 filter_chains。
filter_chain_manager_.addFilterChains 中 检查是否有默认过滤链, 已经把所有过滤链及其匹配条件 filter_chain_match 保存到fc_contexts_。
FilterChainManagerImpl::addFilterChains 如下
解析网络过滤链 filter_chains 配置
void ListenerImpl::buildFilterChains() {
transport_factory_context_->setInitManager(*dynamic_init_manager_);
ListenerFilterChainFactoryBuilder builder(*this, *transport_factory_context_);
filter_chain_manager_.addFilterChains(
config_.filter_chains(),
config_.has_default_filter_chain() ? &config_.default_filter_chain() : nullptr, builder,
filter_chain_manager_);
}
void FilterChainManagerImpl::addFilterChains(
absl::Span<const envoy::config::listener::v3::FilterChain* const> filter_chain_span,
const envoy::config::listener::v3::FilterChain* default_filter_chain,
FilterChainFactoryBuilder& filter_chain_factory_builder,
FilterChainFactoryContextCreator& context_creator) {
Cleanup cleanup(this { origin_ = absl::nullopt; });
absl::node_hash_map<envoy::config::listener::v3::FilterChainMatch, std::string, MessageUtil,
MessageUtil>
filter_chains;
uint32_t new_filter_chain_size = 0;
for (const auto& filter_chain : filter_chain_span) {
const auto& filter_chain_match = filter_chain->filter_chain_match();
if (!filter_chain_match.address_suffix().empty() || filter_chain_match.has_suffix_len()) {
throw EnvoyException(fmt::format("error adding listener ‘{}’: filter chain ‘{}’ contains "
“unimplemented fields”,
address_->asString(), filter_chain->name()));
}
const auto& matching_iter = filter_chains.find(filter_chain_match);
if (matching_iter != filter_chains.end()) {
throw EnvoyException(fmt::format("error adding listener ‘{}’: filter chain ‘{}’ has "
“the same matching rules defined as ‘{}’”,
address_->asString(), filter_chain->name(),
matching_iter->second));
}
filter_chains.insert({filter_chain_match, filter_chain->name()});
auto createAddressVector = [](const auto& prefix_ranges) -> std::vector<std::string> {
std::vector<std::string> ips;
ips.reserve(prefix_ranges.size());
for (const auto& ip : prefix_ranges) {
const auto& cidr_range = Network::Address::CidrRange::create(ip);
ips.push_back(cidr_range.asString());
}
return ips;
};
// Validate IP addresses.
std::vector<std::string> destination_ips =
createAddressVector(filter_chain_match.prefix_ranges());
std::vector<std::string> source_ips =
createAddressVector(filter_chain_match.source_prefix_ranges());
std::vector<std::string> direct_source_ips =
createAddressVector(filter_chain_match.direct_source_prefix_ranges());
std::vector<std::string> server_names;
// Reject partial wildcards, we don't match on them.
for (const auto& server_name : filter_chain_match.server_names()) {
if (server_name.find('*') != std::string::npos && !isWildcardServerName(server_name)) {
throw EnvoyException(
fmt::format("error adding listener '{}': partial wildcards are not supported in "
"\"server_names\"",
address_->asString()));
}
server_names.push_back(absl::AsciiStrToLower(server_name));
}
// Reuse created filter chain if possible.
// FilterChainManager maintains the lifetime of FilterChainFactoryContext
// ListenerImpl maintains the dependencies of FilterChainFactoryContext
auto filter_chain_impl = findExistingFilterChain(*filter_chain);
if (filter_chain_impl == nullptr) {
filter_chain_impl =
filter_chain_factory_builder.buildFilterChain(*filter_chain, context_creator);
++new_filter_chain_size;
}
addFilterChainForDestinationPorts(
destination_ports_map_,
PROTOBUF_GET_WRAPPED_OR_DEFAULT(filter_chain_match, destination_port, 0), destination_ips,
server_names, filter_chain_match.transport_protocol(),
filter_chain_match.application_protocols(), direct_source_ips,
filter_chain_match.source_type(), source_ips, filter_chain_match.source_ports(),
filter_chain_impl);
fc_contexts_[*filter_chain] = filter_chain_impl;
}
convertIPsToTries();
copyOrRebuildDefaultFilterChain(default_filter_chain, filter_chain_factory_builder,
context_creator);
ENVOY_LOG(debug, “new fc_contexts has {} filter chains, including {} newly built”,
fc_contexts_.size(), new_filter_chain_size);
}
- 另外初始化函数InstanceImpl::initialize 中也加载了配置文件中线程数的设置,并且
woker的初始化
void InstanceImpl::initialize(Network::Address::InstanceConstSharedPtr local_address,
ComponentFactory& component_factory) {
//…
// Workers get created first so they register for thread local updates.
listener_manager_ =
std::make_unique(*this, listener_component_factory_, worker_factory_,
bootstrap_.enable_dispatcher_stats(), quic_stat_names_);
//…
}
// ListenerManagerImpl构造函数中初始化 woker 数组 wokers_
ListenerManagerImpl::ListenerManagerImpl(Instance& server,
ListenerComponentFactory& listener_factory,
WorkerFactory& worker_factory,
bool enable_dispatcher_stats,
Quic::QuicStatNames& quic_stat_names)
: server_(server), factory_(listener_factory),
scope_(server.stats().createScope(“listener_manager.”)), stats_(generateStats(*scope_)),
config_tracker_entry_(server.admin().getConfigTracker().add(
“listeners”,
[this](const Matchers::StringMatcher& name_matcher) {
return dumpListenerConfigs(name_matcher);
})),
enable_dispatcher_stats_(enable_dispatcher_stats), quic_stat_names_(quic_stat_names) {
for (uint32_t i = 0; i < server.options().concurrency(); i++) {
workers_.emplace_back(
worker_factory.createWorker(i, server.overloadManager(), absl::StrCat(“worker_”, i)));
}
}
// 创建WorkerImpl实例时 先 构造ConnectionHandlerImpl, 并赋值给 WorkerImpl 的成员变量 handler_
WorkerPtr ProdWorkerFactory::createWorker(uint32_t index, OverloadManager& overload_manager,
const std::string& worker_name) {
Event::DispatcherPtr dispatcher(
api_.allocateDispatcher(worker_name, overload_manager.scaledTimerFactory()));
auto conn_handler = std::make_unique(*dispatcher, index);
return std::make_unique(tls_, hooks_, std::move(dispatcher), std::move(conn_handler),
overload_manager, api_, stat_names_);
}
2.2 启动
InstanceImpl::run()
核心:调用 ListenerManagerImpl::startWorkers,线程绑定 listener 和 启动 woker。
每个线程都绑定了所有的监听器,监听配置的端口,而没有任何分片。创建 socket ,注册事件循环,内核决定新到来的连接 分配给哪个woker线程。一旦 woker accept了一个连接,那么这个连接永远不会离开这个worker 。
启动 woker
void ListenerManagerImpl::startWorkers(GuardDog& guard_dog, std::function<void()> callback) {
// …
// 成员变量 active_listeners_ 在初始化调用 MainImpl::initialize函数中就被配置文件中的listeners 赋值了
// 下面 两重循环将所有的 listener 绑定到所有的 woker 上。
for (auto listener_it = active_listeners_.begin(); listener_it != active_listeners_.end()😉 {
auto& listener = *listener_it;
listener_it++;
if (!doFinalPreWorkerListenerInit(*listener)) {
incListenerCreateFailureStat();
removeListenerInternal(listener->name(), false);
continue;
}
for (const auto& worker : workers_) {
addListenerToWorker(*worker, absl::nullopt, *listener,
this, listeners_pending_init, callback {
if (–(*listeners_pending_init) == 0) {
stats_.workers_started_.set(1);
callback();
}
});
}
}
for (const auto& worker : workers_) {
ENVOY_LOG(debug, “starting worker {}”, i);
worker->start(guard_dog, worker_started_running);
if (enable_dispatcher_stats_) {
worker->initializeStats(*scope_);
}
i++;
}
// …
}
// 将一个ListenerImpl 绑定到 一个 Worker
void ListenerManagerImpl::addListenerToWorker(Worker& worker,
absl::optional<uint64_t> overridden_listener,
ListenerImpl& listener,
ListenerCompletionCallback completion_callback) {
if (overridden_listener.has_value()) {
ENVOY_LOG(debug, “replacing existing listener {}”, overridden_listener.value());
}
worker.addListener(overridden_listener, listener, this, completion_callback -> void {
// The add listener completion runs on the worker thread. Post back to the main thread to
// avoid locking.
server_.dispatcher().post(this, completion_callback -> void {
stats_.listener_create_success_.inc();
if (completion_callback) {
completion_callback();
}
});
});
}
// 用 dispatcher 的post方法 将一个函数对象添加到 woker 的任务队列中,在这个函数对象中核心是handler_->addListener。
// handler_是 woker 的成员变量,负责管理连接。
void WorkerImpl::addListener(absl::optional<uint64_t> overridden_listener,
Network::ListenerConfig& listener, AddListenerCompletion completion) {
dispatcher_->post(this, overridden_listener, &listener, completion -> void {
handler_->addListener(overridden_listener, listener);
hooks_.onWorkerListenerAdded();
completion();
});
}
// 关键在于创建 ActiveTcpListener. 注意WorkerImpl::addListener中,此处 config是 ListenerImpl。
void ConnectionHandlerImpl::addListener(absl::optional<uint64_t> overridden_listener,
Network::ListenerConfig& config) {
//…
// worker_index_ doesn’t have a value on the main thread for the admin server.
auto tcp_listener = std::make_unique(
this, config, worker_index_.has_value() ? worker_index_ : 0);
//…
}
// ActiveTcpListener 构造函数, 他把套接字绑定到了libevent中。
// 核心是 调用了parent.dispatcher().createListener。
ActiveTcpListener::ActiveTcpListener(Network::TcpConnectionHandler& parent,
Network::ListenerConfig& config, uint32_t worker_index)
: OwnedActiveStreamListenerBase(parent, parent.dispatcher(),
parent.dispatcher().createListener(
config.listenSocketFactory().getListenSocket(worker_index),
this, config.bindToPort(), config.ignoreGlobalConnLimit()),
config),
tcp_conn_handler_(parent) {
config.connectionBalancer().registerHandler(this);
}
// 创建 TcpListenerImpl,上面调用此函数时,这里的 socket来自 ListenerImpl::listenSocketFactory。
// 这里的回调函数 cb 来自上面的 this, 也就是ActiveTcpListener自身。*
Network::ListenerPtr DispatcherImpl::createListener(Network::SocketSharedPtr&& socket,
Network::TcpListenerCallbacks& cb,
bool bind_to_port,
bool ignore_global_conn_limit) {
ASSERT(isThreadSafe());
return std::make_uniqueNetwork::TcpListenerImpl(
*this, api_.randomGenerator(), std::move(socket), cb, bind_to_port, ignore_global_conn_limit);
}
// 调用了createFileEvent方法,将socket文件的事件注册到了事件循环中
TcpListenerImpl::TcpListenerImpl(Event::DispatcherImpl& dispatcher, Random::RandomGenerator& random,
SocketSharedPtr socket, TcpListenerCallbacks& cb,
bool bind_to_port, bool ignore_global_conn_limit)
: BaseListenerImpl(dispatcher, std::move(socket)), cb_(cb), random_(random),
bind_to_port_(bind_to_port), reject_fraction_(0.0),
ignore_global_conn_limit_(ignore_global_conn_limit) {
if (bind_to_port) {
// Although onSocketEvent drains to completion, use level triggered mode to avoid potential
// loss of the trigger due to transient accept errors.
socket_->ioHandle().initializeFileEvent(
dispatcher, [this](uint32_t events) -> void { onSocketEvent(events); },
Event::FileTriggerType::Level, Event::FileReadyType::Read);
}
}
void IoSocketHandleImpl::initializeFileEvent(Event::Dispatcher& dispatcher, Event::FileReadyCb cb,
Event::FileTriggerType trigger, uint32_t events) {
ASSERT(file_event_ == nullptr, "Attempting to initialize two file_event_
for the same "
“file descriptor. This is not allowed.”);
file_event_ = dispatcher.createFileEvent(fd_, cb, trigger, events);
}
2.3 接收连接,建立 listener 过滤链和 network 过滤链
ActiveTcpListener::onAccept
void ActiveTcpListener::onAccept(Network::ConnectionSocketPtr&& socket) {
onAcceptWorker(std::move(socket), config_->handOffRestoredDestinationConnections(), false);
}
// 创建 ActiveTcpSocket, 调用
void ActiveTcpListener::onAcceptWorker(Network::ConnectionSocketPtr&& socket,
bool hand_off_restored_destination_connections,
bool rebalanced) {
// …
auto active_socket = std::make_unique(*this, std::move(socket),
hand_off_restored_destination_connections);
onSocketAccepted(std::move(active_socket));
}
void onSocketAccepted(std::unique_ptr active_socket) {
// Create and run the filters
// Network::ListenerImpl::filterChainFactory:返回 this
config_->filterChainFactory().createListenerFilterChain(*active_socket);
active_socket->continueFilterChain(true);
// …
}
上面 onSocketAccepted 内容:
(1)ListenerImpl::createListenerFilterChain → Configuration::FilterChainUtility::buildFilterChain(manager, listener_filter_factories_)
创建 listener 过滤器的 filter chain ,
(2)ActiveTcpSocket::continueFilterChain→ newConnection 建立连接,运行网络过滤器,如下
continueFilterChain
void ActiveTcpSocket::continueFilterChain(bool success) {
// …
// 遍历监听器过滤器
for (; iter_ != accept_filters_.end(); iter_++) {
Network::FilterStatus status = (*iter_)->onAccept(*this);
if (status == Network::FilterStatus::StopIteration) {
// The filter is responsible for calling us again at a later time to continue the filter
// chain from the next filter.
if (!socket().ioHandle().isOpen()) {
// break the loop but should not create new connection
no_error = false;
break;
} else {
// Blocking at the filter but no error
return;
}
}
}
// …
newConnection();
//…
}
void ActiveTcpSocket::newConnection() {
// …
listener_.newConnection(std::move(socket_), std::move(stream_info_));
}
// 下面 函数中 和 createNetworkFilterChain
void ActiveStreamListenerBase::newConnection(Network::ConnectionSocketPtr&& socket,
std::unique_ptrStreamInfo::StreamInfo stream_info) {
// Find matching filter chain.
const auto filter_chain = config_->filterChainManager().findFilterChain(*socket);
if (filter_chain == nullptr) {
RELEASE_ASSERT(socket->connectionInfoProvider().remoteAddress() != nullptr, “”);
ENVOY_LOG(debug, “closing connection from {}: no matching filter chain found”,
socket->connectionInfoProvider().remoteAddress()->asString());
stats_.no_filter_chain_match_.inc();
stream_info->setResponseFlag(StreamInfo::ResponseFlag::NoRouteFound);
stream_info->setResponseCodeDetails(StreamInfo::ResponseCodeDetails::get().FilterChainNotFound);
emitLogs(*config_, *stream_info);
socket->close();
return;
}
stream_info->setFilterChainName(filter_chain->name());
auto transport_socket = filter_chain->transportSocketFactory().createTransportSocket(nullptr);
// 调用createServerConnection 建立请求端和 envoy server 的连接
auto server_conn_ptr = dispatcher().createServerConnection(
std::move(socket), std::move(transport_socket), *stream_info);
if (const auto timeout = filter_chain->transportSocketConnectTimeout();
timeout != std::chrono::milliseconds::zero()) {
server_conn_ptr->setTransportSocketConnectTimeout(
timeout, stats_.downstream_cx_transport_socket_connect_timeout_);
}
server_conn_ptr->setBufferLimits(config_->perConnectionBufferLimitBytes());
RELEASE_ASSERT(server_conn_ptr->connectionInfoProvider().remoteAddress() != nullptr, “”);
// 调用 ListenerImpl::createNetworkFilterChain 建立网络过滤器链
const bool empty_filter_chain = !config_->filterChainFactory().createNetworkFilterChain(
*server_conn_ptr, filter_chain->networkFilterFactories());
if (empty_filter_chain) {
ENVOY_CONN_LOG(debug, “closing connection from {}: no filters”, *server_conn_ptr,
server_conn_ptr->connectionInfoProvider().remoteAddress()->asString());
server_conn_ptr->close(Network::ConnectionCloseType::NoFlush);
}
newActiveConnection(*filter_chain, std::move(server_conn_ptr), std::move(stream_info));
}
// 上面的 newActiveConnection 来自
void ActiveTcpListener::newActiveConnection(const Network::FilterChain& filter_chain,
Network::ServerConnectionPtr server_conn_ptr,
std::unique_ptrStreamInfo::StreamInfo stream_info) {
auto& active_connections = getOrCreateActiveConnections(filter_chain);
auto active_connection =
std::make_unique(active_connections, std::move(server_conn_ptr),
dispatcher().timeSource(), std::move(stream_info));
// If the connection is already closed, we can just let this connection immediately die.
if (active_connection->connection_->state() != Network::Connection::State::Closed) {
ENVOY_CONN_LOG(
debug, “new connection from {}”, *active_connection->connection_,
active_connection->connection_->connectionInfoProvider().remoteAddress()->asString());
active_connection->connection_->addConnectionCallbacks(*active_connection);
LinkedList::moveIntoList(std::move(active_connection), active_connections.connections_);
}
}
上面 ActiveStreamListenerBase::newConnection 主要内容:
(1)createServerConnection:建立请求端和 envoy server 的连接,返回ServerConnectionImpl ,在这里设置 buffer 的高低水位来进行流量控制,并且创建 FileEvent 。
ServerConnectionImpl 继承ConnectionImpl, 而 ConnectionImpl的构造函数调用 socket_→ioHandle().initializeFileEvent ,如下。回调函数 为 ConnectionImpl::onFileEvent ,当读和写都就绪时 返回 onReadReady() 和 onWriteReady() 。
ConnectionImpl 构造函数
ConnectionImpl::ConnectionImpl(Event::Dispatcher& dispatcher, ConnectionSocketPtr&& socket,
TransportSocketPtr&& transport_socket,
StreamInfo::StreamInfo& stream_info, bool connected)
: ConnectionImplBase(dispatcher, next_global_id_++),
transport_socket_(std::move(transport_socket)), socket_(std::move(socket)),
stream_info_(stream_info), filter_manager_(*this, socket_),
write_buffer_(dispatcher.getWatermarkFactory().createBuffer(
this -> void { this->onWriteBufferLowWatermark(); },
this -> void { this->onWriteBufferHighWatermark(); },
-> void { / TODO(adisuissa): Handle overflow watermark / })),
read_buffer_(dispatcher.getWatermarkFactory().createBuffer(
this -> void { this->onReadBufferLowWatermark(); },
this -> void { this->onReadBufferHighWatermark(); },
-> void { / TODO(adisuissa): Handle overflow watermark */ })),
write_buffer_above_high_watermark_(false), detect_early_close_(true),
enable_half_close_(false), read_end_stream_raised_(false), read_end_stream_(false),
write_end_stream_(false), current_write_end_stream_(false), dispatch_buffered_data_(false),
transport_wants_read_(false) {
if (!connected) {
connecting_ = true;
}
Event::FileTriggerType trigger = Event::PlatformDefaultTriggerType;
// We never ask for both early close and read at the same time. If we are reading, we want to
// consume all available data.
socket_->ioHandle().initializeFileEvent(
dispatcher_, [this](uint32_t events) -> void { onFileEvent(events); }, trigger,
Event::FileReadyType::Read | Event::FileReadyType::Write);
transport_socket_->setTransportSocketCallbacks(*this);
// TODO(soulxu): generate the connection id inside the addressProvider directly,
// then we don’t need a setter or any of the optional stuff.
socket_->connectionInfoProvider().setConnectionID(id());
socket_->connectionInfoProvider().setSslConnection(transport_socket_->ssl());
}
initializeFileEvent 函数中 创建 FileEvent (创建新的 FileEventImpl),文件事件对象中会明确 通过 event_add 将事件对象注册到事件循环中,明确事件触发时的回调函数,事件触发的类型 。
(2)createNetworkFilterChain : 建立 network 的 过滤器链。
(3)FilterManagerImpl::onContinueReading : 请求数据的处理流程拼装。
当从fd中拿到数据后,则会进行正式的处理。FilterManager管理所有的Read/Write Filter,并拼装成pipeline进行处理。关键在于 Filter 的 onNewConnection 和 onData 两个函数。
createNetworkFilterChain
bool ListenerImpl::createNetworkFilterChain(
Network::Connection& connection,
const std::vectorNetwork::FilterFactoryCb& filter_factories) {
return Configuration::FilterChainUtility::buildFilterChain(connection, filter_factories);
}
bool FilterChainUtility::buildFilterChain(Network::FilterManager& filter_manager,
const std::vectorNetwork::FilterFactoryCb& factories) {
for (const Network::FilterFactoryCb& factory : factories) {
factory(filter_manager);
}
return filter_manager.initializeReadFilters();
}
bool FilterManagerImpl::initializeReadFilters() {
if (upstream_filters_.empty()) {
return false;
}
onContinueReading(nullptr, connection_);
return true;
}
void FilterManagerImpl::onContinueReading(ActiveReadFilter* filter,
ReadBufferSource& buffer_source) {
// Filter could return status == FilterStatus::StopIteration immediately, close the connection and
// use callback to call this function.
if (connection_.state() != Connection::State::Open) {
return;
}
std::list::iterator entry;
if (!filter) {
connection_.streamInfo().addBytesReceived(buffer_source.getReadBuffer().buffer.length());
entry = upstream_filters_.begin();
} else {
entry = std::next(filter->entry());
}
for (; entry != upstream_filters_.end(); entry++) {
if (!(*entry)->filter_) {
continue;
}
if (!(*entry)->initialized_) {
(*entry)->initialized_ = true;
// 第一次访问则调用onNewConnection
FilterStatus status = (*entry)->filter_->onNewConnection();
if (status == FilterStatus::StopIteration || connection_.state() != Connection::State::Open) {
return;
}
}
StreamBuffer read_buffer = buffer_source.getReadBuffer();
if (read_buffer.buffer.length() > 0 || read_buffer.end_stream) {
// 后续访问则调用onData
FilterStatus status = (*entry)->filter_->onData(read_buffer.buffer, read_buffer.end_stream);
if (status == FilterStatus::StopIteration || connection_.state() != Connection::State::Open) {
return;
}
}
}
}
在 Envoy 程序启动阶段,配置工厂对象被注册到一个全局 Map 中,这样就可以在就可以通过配置来控制 Envoy 的行为了。REGISTER_FACTORY 是一个宏定义,也可以直接调用 Registry::RegisterFactory() 在程序初始化阶段注册 Filter 的配置工厂对象。配置工厂对象注册完成后,就可以根据一个name,比如 envoy.filters.network.echo得到配置工厂对象,然后初始化对应的Filter实例
过滤器注册
REGISTER_FACTORY(ConnectionLimitConfigFactory,
Server::Configuration::NamedNetworkFilterConfigFactory);
/** envoy/envoy/registry/registry.h
- Macro used for static registration.
/
#define REGISTER_FACTORY(FACTORY, BASE)
ABSL_ATTRIBUTE_UNUSED void forceRegister##FACTORY() {}
static Envoy::Registry::RegisterFactory</ NOLINT(fuchsia-statically-constructed-objects) */
FACTORY, BASE>
FACTORY##_registered
2.4 envoy 建立连接流程图
- 网络过滤器
3.1 介绍
根据行为的不同,网络过滤器分为:
-
读过滤器,当Envoy从下游连接接收到流量时调用
-
写过滤器,当Envoy准备向下游连接发送流量时调用
-
读/写过滤器,在上述两种情况下均调用
由于网络过滤器操控套接字的原始字节(外加少量事件,例如TLS握手完毕、连接断开),因此它的接口比较简单。
每个过滤器都可以中止迭代流程,并在未来继续后续过滤器的迭代。这种中止/继续迭代的机制,让实现复杂的需求成为可能,例如调用限速服务,异步的根据调用结果决定是否继续迭代。
过滤器之间可以通过过滤器状态(Filter State)的机制彼此共享数据 。
网络过滤器的接口只有4个方法。
位置:envoy/network/filter.h
读过滤器
class ReadFilter {
public:/**
- 当连接上的数据被读取时调用
- @param data 读取到的,可能已经被修改过的数据
- @param end_stream 当连接启用半关闭语义时,用于提示是否到了最后一字节
- @return status 过滤器管理器使用此状态决定如何进一步迭代其它过滤器
*/
virtual FilterStatus onData(Buffer::Instance& data, bool end_stream) PURE;
/**
- 当新连接刚创建时调用,过滤器链的迭代可以被中止
- @return status 过滤器管理器使用此状态决定如何进一步迭代其它过滤器
*/
virtual FilterStatus onNewConnection() PURE;
/**
- 初始化用于和过滤器管理器交互的读过滤器回调,过滤器被注册时,将被过滤器管理器调用一次
- 任何需要用到底层连接的构造,需要在此函数的回调中执行
- IMPORTANT: 出站、复杂逻辑不要在此,放在onNewConnection()
/
virtual void initializeReadFilterCallbacks(ReadFilterCallbacks& callbacks) PURE;
}
写过滤器
class WriteFilter {
public:
virtual ~WriteFilter() = default;
/*- 当在此连接上发生数据写入时调用
- @param data 需要写入的,可能已经被修改过的数据
- @param end_stream 当连接启用半关闭语义时,用于提示是否到了最后一字节
*/
virtual FilterStatus onWrite(Buffer::Instance& data, bool end_stream) PURE;
virtual void initializeWriteFilterCallbacks(WriteFilterCallbacks&) {}
};
读/写过滤器
class Filter : public WriteFilter, public ReadFilter {};
using FilterSharedPtr = std::shared_ptr;
3.2 示例:网络读过滤器 echo
网络 读过滤器 echo, 实现如下
source/extensions/filters/network/echo/echo.h
#pragma once
#include “envoy/network/filter.h”
#include “source/common/common/logger.h”
namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace Echo {
/**
- Implementation of a basic echo filter.
*/
class EchoFilter : public Network::ReadFilter, Logger::LoggableLogger::Id::filter {
public:
// Network::ReadFilter
Network::FilterStatus onData(Buffer::Instance& data, bool end_stream) override;
// 新连接到达后不做任何处理,继续调用下一个过滤器
Network::FilterStatus onNewConnection() override { return Network::FilterStatus::Continue; }
// 初始化回调集
void initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) override {
read_callbacks_ = &callbacks;
}
private:
Network::ReadFilterCallbacks* read_callbacks_{};
};
} // namespace Echo
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
source/extensions/filters/network/echo/echo.cc
#include “source/extensions/filters/network/echo/echo.h”
#include “envoy/buffer/buffer.h”
#include “envoy/network/connection.h”
#include “source/common/common/assert.h”
namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace Echo {
Network::FilterStatus EchoFilter::onData(Buffer::Instance& data, bool end_stream) {
// 接收到下游发来的数据后,简单的记录日志
ENVOY_CONN_LOG(trace, “echo: got {} bytes”, read_callbacks_->connection(), data.length());
// 并把收到的数据直接Echo给下游
read_callbacks_->connection().write(data, end_stream);
// 然后停止过滤器迭代,不调用它们
ASSERT(0 == data.length());
return Network::FilterStatus::StopIteration;
}
} // namespace Echo
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
每个过滤器都需要以一个独特的名称进行注册,否则Envoy无法知道它的存在,你也不能在配置文件中引用它。每个过滤器 除了实现代码 .h 和 .cc 文件, 都还有一个 config.cc 配置文件,这里面提供了 根据过滤器名称 注册 过滤器的 方法。
source/extensions/filters/network/echo/config.cc
#include “envoy/extensions/filters/network/echo/v3/echo.pb.h”
#include “envoy/extensions/filters/network/echo/v3/echo.pb.validate.h”
#include “envoy/registry/registry.h”
#include “envoy/server/filter_config.h”
#include “source/extensions/filters/network/common/factory_base.h”
#include “source/extensions/filters/network/echo/echo.h”
#include “source/extensions/filters/network/well_known_names.h”
namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace Echo {
/**
- Config registration for the echo filter. @see NamedNetworkFilterConfigFactory.
*/
class EchoConfigFactory
: public Common::FactoryBaseenvoy::extensions::filters::network::echo::v3::Echo {
public:
EchoConfigFactory() : FactoryBase(NetworkFilterNames::get().Echo) {}
private:
Network::FilterFactoryCb
createFilterFactoryFromProtoTyped(const envoy::extensions::filters::network::echo::v3::Echo&,
Server::Configuration::FactoryContext&) override {
// 过滤器工厂回调,初始化过滤器链时,Envoy会调用此方法
// 通常会在这里实例化过滤器,并添加到过滤器管理器中
return [](Network::FilterManager& filter_manager) -> void {
filter_manager.addReadFilter(std::make_shared());
};
}
bool isTerminalFilterByProtoTyped(const envoy::extensions::filters::network::echo::v3::Echo&,
Server::Configuration::FactoryContext&) override {
return true;
}
};
/**
- Static registration for the echo filter. @see RegisterFactory.
*/
REGISTER_FACTORY(EchoConfigFactory,
Server::Configuration::NamedNetworkFilterConfigFactory){“envoy.echo”};
} // namespace Echo
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
proto3 定义 过滤器的配置,这个 message 会被转成 class (参考 经过编译生成的 .pb.h 和 .pb.cc 文件)。
proto3
/*** api/envoy/extensions/filters/network/echo/v3/echo.proto ***/
syntax = “proto3”;
package envoy.extensions.filters.network.echo.v3;
import “udpa/annotations/status.proto”;
import “udpa/annotations/versioning.proto”;
option java_package = “io.envoyproxy.envoy.extensions.filters.network.echo.v3”;
option java_outer_classname = “EchoProto”;
option java_multiple_files = true;
option go_package = “github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/echo/v3;echov3”;
option (udpa.annotations.file_status).package_version_status = ACTIVE;
// [#protodoc-title: Echo]
// Echo :ref:configuration overview <config_network_filters_echo>
.
// [#extension: envoy.filters.network.echo]
message Echo {
option (udpa.annotations.versioning).previous_message_type =
“envoy.config.filter.network.echo.v2.Echo”;
}
/*** api/envoy/config/filter/network/echo/v2/echo.proto ***/
syntax = “proto3”;
package envoy.config.filter.network.echo.v2;
import “udpa/annotations/migrate.proto”;
import “udpa/annotations/status.proto”;
option java_package = “io.envoyproxy.envoy.config.filter.network.echo.v2”;
option java_outer_classname = “EchoProto”;
option java_multiple_files = true;
option go_package = “github.com/envoyproxy/go-control-plane/envoy/config/filter/network/echo/v2;echov2”;
option (udpa.annotations.file_migrate).move_to_package = “envoy.extensions.filters.network.echo.v3”;
option (udpa.annotations.file_status).package_version_status = FROZEN;
// [#protodoc-title: Echo]
// Echo :ref:configuration overview <config_network_filters_echo>
.
// [#extension: envoy.filters.network.echo]
message Echo {
}
定义 网络过滤器的名称 。
source/extensions/filters/network/well_known_names.h
#pragma once
#include “source/common/config/well_known_names.h”
namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
/**
- Well-known network filter names.
- NOTE: New filters should use the well known name: envoy.filters.network.name.
*/
class NetworkFilterNameValues {
public:
// Client ssl auth filter
const std::string ClientSslAuth = “envoy.filters.network.client_ssl_auth”;
// Connection limit filter
const std::string ConnectionLimit = “envoy.filters.network.connection_limit”;
// Echo filter
const std::string Echo = “envoy.filters.network.echo”;
// Direct response filter
const std::string DirectResponse = “envoy.filters.network.direct_response”;
// RocketMQ proxy filter
const std::string RocketmqProxy = “envoy.filters.network.rocketmq_proxy”;
// Dubbo proxy filter
const std::string DubboProxy = “envoy.filters.network.dubbo_proxy”;
// Envoy mobile http connection manager.
const std::string EnvoyMobileHttpConnectionManager =
“envoy.filters.network.envoy_mobile_http_connection_manager”;
// HTTP connection manager filter
const std::string HttpConnectionManager = “envoy.filters.network.http_connection_manager”;
// Local rate limit filter
const std::string LocalRateLimit = “envoy.filters.network.local_ratelimit”;
// Mongo proxy filter
const std::string MongoProxy = “envoy.filters.network.mongo_proxy”;
// MySQL proxy filter
const std::string MySQLProxy = “envoy.filters.network.mysql_proxy”;
// Postgres proxy filter
const std::string PostgresProxy = “envoy.filters.network.postgres_proxy”;
// Rate limit filter
const std::string RateLimit = “envoy.filters.network.ratelimit”;
// Redis proxy filter
const std::string RedisProxy = “envoy.filters.network.redis_proxy”;
// TCP proxy filter
const std::string TcpProxy = “envoy.filters.network.tcp_proxy”;
// Authorization filter
const std::string ExtAuthorization = “envoy.filters.network.ext_authz”;
// Kafka Broker filter
const std::string KafkaBroker = “envoy.filters.network.kafka_broker”;
// Thrift proxy filter
const std::string ThriftProxy = “envoy.filters.network.thrift_proxy”;
// Role based access control filter
const std::string Rbac = “envoy.filters.network.rbac”;
// SNI Cluster filter
const std::string SniCluster = “envoy.filters.network.sni_cluster”;
// SNI Dynamic forward proxy filter
const std::string SniDynamicForwardProxy = “envoy.filters.network.sni_dynamic_forward_proxy”;
// ZooKeeper proxy filter
const std::string ZooKeeperProxy = “envoy.filters.network.zookeeper_proxy”;
// WebAssembly filter
const std::string Wasm = “envoy.filters.network.wasm”;
};
using NetworkFilterNames = ConstSingleton;
} // namespace NetworkFilters
} // namespace Extensions
/*** source/common/singleton/const_singleton.h */
template class ConstSingleton {
public:
/
- Obtain an instance of the singleton for class T.
- @return const T& a reference to the singleton for class T.
/
static const T& get() {
static T instance = new T();
return *instance;
}
};
} // namespace Envoy
网络过滤器 配置工厂(比如 EchoConfigFactory) 的 基类 FactoryBase
source/extensions/filters/network/common/factory_base.h
#pragma once
#include “envoy/server/filter_config.h”
#include “envoy/server/transport_socket_config.h”
#include “envoy/upstream/upstream.h”
#include “source/common/common/utility.h”
namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace Common {
/**
- Common base class for network filter factory registrations. Removes a substantial amount of
- boilerplate.
*/
template <class ConfigProto, class ProtocolOptionsProto = ConfigProto>
class FactoryBase : public Server::Configuration::NamedNetworkFilterConfigFactory {
public:
Network::FilterFactoryCb
createFilterFactoryFromProto(const Protobuf::Message& proto_config,
Server::Configuration::FactoryContext& context) override {
return createFilterFactoryFromProtoTyped(MessageUtil::downcastAndValidate<const ConfigProto&>(
proto_config, context.messageValidationVisitor()),
context);
}
ProtobufTypes::MessagePtr createEmptyConfigProto() override {
return std::make_unique();
}
ProtobufTypes::MessagePtr createEmptyProtocolOptionsProto() override {
return std::make_unique();
}
Upstream::ProtocolOptionsConfigConstSharedPtr createProtocolOptionsConfig(
const Protobuf::Message& proto_config,
Server::Configuration::ProtocolOptionsFactoryContext& factory_context) override {
return createProtocolOptionsTyped(MessageUtil::downcastAndValidate<const ProtocolOptionsProto&>(
proto_config, factory_context.messageValidationVisitor()),
factory_context);
}
std::string name() const override { return name_; }
bool isTerminalFilterByProto(const Protobuf::Message& proto_config,
Server::Configuration::FactoryContext& context) override {
return isTerminalFilterByProtoTyped(MessageUtil::downcastAndValidate<const ConfigProto&>(
proto_config, context.messageValidationVisitor()),
context);
}
protected:
FactoryBase(const std::string& name, bool is_terminal = false)
: name_(name), is_terminal_filter_(is_terminal) {}
private:
virtual bool isTerminalFilterByProtoTyped(const ConfigProto&,
Server::Configuration::FactoryContext&) {
return is_terminal_filter_;
}
virtual Network::FilterFactoryCb
createFilterFactoryFromProtoTyped(const ConfigProto& proto_config,
Server::Configuration::FactoryContext& context) PURE;
virtual Upstream::ProtocolOptionsConfigConstSharedPtr
createProtocolOptionsTyped(const ProtocolOptionsProto&,
Server::Configuration::ProtocolOptionsFactoryContext&) {
ExceptionUtil::throwEnvoyException(
fmt::format(“filter {} does not support protocol options”, name_));
}
const std::string name_;
const bool is_terminal_filter_;
};
} // namespace Common
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
通过在 envoy 配置文件中 指定 过滤器名称来使用这个过滤器。如果proto3定义的message 中 这个过滤器有参数,那么也可以在 配置文件中定义参数。
envoy配置文件,使用echo过滤器
static_resources:
listeners: #配置侦听器
- name: listener_0 #侦听器的名称
address: #配置侦听器的地址信息
socket_address:
address: 0.0.0.0 #侦听的ip地址
port_value: 8080 #侦听的端口
filter_chains: #配置侦听器的filter chains- filters:
- name: envoy.filters.network.echo #使用什么filter,这里是使用的echo filter
- filters:
3.3 总结:开发一个新网络过滤器的步骤
(1)首先 实现过滤器的代码,.h 和 .cc 文件。
(2)proto3 写好 过滤器的配置 message 。
(3) 在 well_know_names.h 中 定义这个过滤器的名称。
(4) 写 过滤器 配置工厂, config.cc 文件。
4 HTTP 过滤器
4.1 介绍
HTTP过滤器类似于网络过滤器,也是形成一个栈。HTTP过滤器栈由HttpConnectionManager管理,HttpConnectionManager是一个L4过滤器。
根据行为的不同,HTTP过滤器分为:
- 解码器(Decoder),当HTTP连接管理器解码请求流 时调用
- 编码器(Encoder),当HTTP连接管理器准备编码响应流时调用
- 编解码器,在上述两种情况下均调用
需要注意,HTTP过滤器操作的对象是流,而不是连接: - 对于HTTP1.1,在任意时间点每个连接上最多有一个流
- 对于HTTP2或者gRPC,实现了连接的多路复用,允许多个流同时依托于单个L4连接
接口
HTTP过滤器接口屏蔽了L4协议的细节。和L4过滤器一样,HTTP过滤器也能够中止、继续过滤器迭代,各HTTP过滤器同样可以在同一个请求流的上下文内共享一些静态或动态数据。
L7过滤器的类图如下:
HTTP流编解码器公共的父接口:
envoy/http/filter.h StreamFilterBase
class StreamFilterBase {
public:
/**
- 当过滤器将要被销毁时调用,销毁可能在流正常结束后,或者因为RESET提前发生
- 任何过滤器都应在此方法中确保,所有异步事件 —— 例如定时器、网络调用 —— 被清理干净
*/
virtual void onDestroy() PURE;
};
HTTP流解码器,负责处理下游发来的请求:
envoy/http/filter.h StreamDecoderFilter
class StreamDecoderFilter : public StreamFilterBase {
public:
/**
- 处理已经被http_parser解析好的请求头
- @param headers 请求头的映射
- @param end_stream 提示当前流是否header-only的
- @return FilterHeadersStatus 用于确定是否继续迭代后续过滤器
*/
virtual FilterHeadersStatus decodeHeaders(HeaderMap& headers, bool end_stream) PURE;
/**
- 处理已经被http_parser解析好的数据帧
- @param data 存放数据帧的缓冲区
- @param end_stream 提示当前是否最后一个数据帧
- @return FilterDataStatus 用于确定是否继续迭代后续过滤器
*/
virtual FilterDataStatus decodeData(Buffer::Instance& data, bool end_stream) PURE;
/**
- 处理已经被http_parser解析好的请求尾,隐含end_stream = true
- @param trailers supplies the decoded trailers.
*/
virtual FilterTrailersStatus decodeTrailers(HeaderMap& trailers) PURE;
/**
- 过滤器管理器调用此方法来初始化解码回调集
/
virtual void setDecoderFilterCallbacks(StreamDecoderFilterCallbacks& callbacks) PURE;
};
HTTP流编码器,可以处理准备发给下游的响应:
envoy/http/filter.h StreamEncoderFilter
class StreamEncoderFilter : public StreamFilterBase {
public:
/ - 当配置Envoy,让其代理(通常不会配置)Expect:100-continue请求,
- 并且当前请求指定了Expect:100-continue时,会调用此方法
*/
virtual FilterHeadersStatus encode100ContinueHeaders(HeaderMap& headers) PURE;
/**
- 处理响应头
*/
virtual FilterHeadersStatus encodeHeaders(HeaderMap& headers, bool end_stream) PURE;
/**
- 处理响应体
*/
virtual FilterDataStatus encodeData(Buffer::Instance& data, bool end_stream) PURE;
/**
- 处理响应尾,隐含end_stream = true
*/
virtual FilterTrailersStatus encodeTrailers(HeaderMap& trailers) PURE;
/**
- 处理元数据,新的元数据应该直接存入metadata_map
- 不要通过StreamDecoderFilterCallbacks::encodeMetadata()来添加元数据
*/
virtual FilterMetadataStatus encodeMetadata(MetadataMap& metadata_map) PURE;
/**
- 滤器管理器调用此方法来初始化编码回调集
*/
virtual void setEncoderFilterCallbacks(StreamEncoderFilterCallbacks& callbacks) PURE;
};
HTTP流编码器,可编码可解码。
envoy/http/filter.h StreamFilter
class StreamFilter : public virtual StreamDecoderFilter, public virtual StreamEncoderFilter {};
4.2 连接管理器 介绍
专门处理 http 协议的 network filter,内部又实现了 http filter。
处理包体数据的接口 onData 如下
source/common/http/conn_manager_impl.cc
Network::FilterStatus ConnectionManagerImpl::onData(Buffer::Instance& data, bool) {
if (!codec_) {
// Http3 codec should have been instantiated by now.
// 初次访问时,codec会被初始化出来。codec是ServerConnection类型。会承担编解码的任务。
createCodec(data);
}
bool redispatch;
do {
redispatch = false;
// 这边即会进行真实的解码过程。
const Status status = codec_->dispatch(data);
if (isBufferFloodError(status) || isInboundFramesWithEmptyPayloadError(status)) {
handleCodecError(status.message());
return Network::FilterStatus::StopIteration;
} else if (isCodecProtocolError(status)) {
stats_.named_.downstream_cx_protocol_error_.inc();
handleCodecError(status.message());
return Network::FilterStatus::StopIteration;
}
ASSERT(status.ok());
// Processing incoming data may release outbound data so check for closure here as well.
checkForDeferredClose();
// The HTTP/1 codec will pause dispatch after a single message is complete. We want to
// either redispatch if there are no streams and we have more data. If we have a single
// complete non-WebSocket stream but have not responded yet we will pause socket reads
// to apply back pressure.
if (codec_->protocol() < Protocol::Http2) {
if (read_callbacks_->connection().state() == Network::Connection::State::Open &&
data.length() > 0 && streams_.empty()) {
redispatch = true;
}
}
} while (redispatch);
if (!read_callbacks_->connection().streamInfo().protocol()) {
read_callbacks_->connection().streamInfo().protocol(codec_->protocol());
}
return Network::FilterStatus::StopIteration;
}
http流解码
Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) {
// …
ssize_t total_parsed = 0;
if (data.length() > 0) {
current_dispatching_buffer_ = &data;
while (data.length() > 0) {
auto slice = data.frontSlice();
dispatching_slice_already_drained_ = false;
// 对buffer数据进行分片处理
auto statusor_parsed = dispatchSlice(static_cast<const char*>(slice.mem_), slice.len_);
if (!statusor_parsed.ok()) {
return statusor_parsed.status();
}
if (!dispatching_slice_already_drained_) {
ASSERT(statusor_parsed.value() <= slice.len_);
// 将buffer下标前移,代表已经消费完了指定byte长度的数据。
data.drain(statusor_parsed.value());
}
total_parsed += statusor_parsed.value();
if (parser_->getStatus() != ParserStatus::Success) {
// Parse errors trigger an exception in dispatchSlice so we are guaranteed to be paused at
// this point.
ASSERT(parser_->getStatus() == ParserStatus::Paused);
break;
}
}
current_dispatching_buffer_ = nullptr;
dispatchBufferedBody();
} else {
auto result = dispatchSlice(nullptr, 0);
if (!result.ok()) {
return result.status();
}
}
//…
return Http::okStatus();
}
Envoy::StatusOr<size_t> ConnectionImpl::dispatchSlice(const char* slice, size_t len) {
ASSERT(codec_status_.ok() && dispatching_);
// 真实解码的过程在这里
auto [nread, rc] = parser_->execute(slice, len);
if (!codec_status_.ok()) {
return codec_status_;
}
if (rc != parser_->statusToInt(ParserStatus::Success) &&
rc != parser_->statusToInt(ParserStatus::Paused)) {
RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().HttpCodecError));
// Avoid overwriting the codec_status_ set in the callbacks.
ASSERT(codec_status_.ok());
codec_status_ =
codecProtocolError(absl::StrCat("http/1.1 protocol error: ", parser_->errnoName(rc)));
return codec_status_;
}
return nread;
}
// 下面调用了 http_parser库来进行处理
RcVal execute(const char* slice, int len) {
return {http_parser_execute(&parser_, &settings_, slice, len), HTTP_PARSER_ERRNO(&parser_)};
}
http_parser库解析http流
http_parser_settings ConnectionImpl::settings_{
[](http_parser* parser) -> int {
static_cast<ConnectionImpl*>(parser->data)->onMessageBeginBase();
return 0;
},
[](http_parser* parser, const char* at, size_t length) -> int {
static_cast<ConnectionImpl*>(parser->data)->onUrl(at, length);
return 0;
},
nullptr, // on_status
[](http_parser* parser, const char* at, size_t length) -> int {
static_cast<ConnectionImpl*>(parser->data)->onHeaderField(at, length);
return 0;
},
[](http_parser* parser, const char* at, size_t length) -> int {
static_cast<ConnectionImpl*>(parser->data)->onHeaderValue(at, length);
return 0;
},
[](http_parser* parser) -> int {
return static_cast<ConnectionImpl*>(parser->data)->onHeadersCompleteBase();
},
[](http_parser* parser, const char* at, size_t length) -> int {
static_cast<ConnectionImpl*>(parser->data)->onBody(at, length);
return 0;
},
[](http_parser* parser) -> int {
static_cast<ConnectionImpl*>(parser->data)->onMessageCompleteBase();
return 0;
},
nullptr, // on_chunk_header
nullptr // on_chunk_complete
};
报文会按照 url, header, body 的顺序依次进行解析,之后达到 onMessageCompleteBase()。
onMessageComplete() 所属类是 ServerConnectionImpl, 这里会通过 decodeHeaders 解码请求的头文件。
ParserStatus ServerConnectionImpl::onMessageCompleteBase() {
ASSERT(!handling_upgrade_);
if (active_request_) {
// …
if (deferred_end_stream_headers_) {
// 注意这里
active_request_->request_decoder_->decodeHeaders(
std::move(absl::get(headers_or_trailers_)), true);
deferred_end_stream_headers_ = false;
} else if (processing_trailers_) {
active_request_->request_decoder_->decodeTrailers(
std::move(absl::get(headers_or_trailers_)));
} else {
Buffer::OwnedImpl buffer;
// 注意这里
active_request_->request_decoder_->decodeData(buffer, true);
}
headers_or_trailers_.emplace(nullptr);
}
// …
}
// 在以下两个函数中 调用的 FilterManager::decodeHeaders 和 FilterManager::decodeData 会 去依次执行HTTP过滤链的这两个接口。
void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapPtr&& headers,
bool end_stream) {
//…
filter_manager_.decodeHeaders(*request_headers_, end_stream);
// …
}
void ConnectionManagerImpl::ActiveStream::decodeData(Buffer::Instance& data, bool end_stream) {
ScopeTrackerScopeState scope(this,
connection_manager_.read_callbacks_->connection().dispatcher());
filter_manager_.maybeEndDecode(end_stream);
filter_manager_.streamInfo().addBytesReceived(data.length());
filter_manager_.decodeData(data, end_stream);
}
4.4 示例
见 5.1
4.5 总结
开发一个新的 http 过滤器的步骤与 网络过滤器的开发流程差不多,不过需要实现的接口不一样。
5 AIEdge 中对过滤器的使用和配置
5.1 lua
AIEdge 网关函数详细设计
Lua 过滤器现在只在 HTTP 上运作。你可以在 Envoy config 配置中编写和嵌入 Lua 脚本,然后 Lua 过滤器就会允许你进行 header 和数据修改。
5.1.1 lua 配置举例
参考 https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter
name: envoy.filters.http.lua
typed_config:
“@type”: type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua # 这里明确指定为 envoy/api 中 proto 定义的具体 message
inline_code: |
– Called on the request path.
function envoy_on_request(request_handle)
– Do something.
end
– Called on the response path.
function envoy_on_response(response_handle)
– Do something.
end
// **** 或者
source_codes:
hello.lua:
inline_string: |
function envoy_on_request(request_handle)
request_handle:logInfo(“Hello World.”)
end
bye.lua:
inline_string: |
function envoy_on_response(response_handle)
response_handle:logInfo(“Bye Bye.”)
end
// *** 或者
source_codes:
hello.lua:
inline_string: |
function envoy_on_response(response_handle)
– Do something.
end
world.lua:
filename: /etc/lua/world.lua
5.1.2 lua 源码简析
source/extensions/filters/http/lua
class Filter : public Http::StreamFilter, Logger::LoggableLogger::Id::lua {
public:
Filter(FilterConfigConstSharedPtr config, TimeSource& time_source)
: config_(config), time_source_(time_source) {}
// Http::StreamDecoderFilter
Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers,
bool end_stream) override {
// getPerLuaCodeSetup 拿到具体的 lua 脚本
PerLuaCodeSetup* setup = getPerLuaCodeSetup(config_.get(), decoder_callbacks_.callbacks_);
const int function_ref = setup ? setup->requestFunctionRef() : LUA_REFNIL;
return doHeaders(request_stream_wrapper_, request_coroutine_, decoder_callbacks_, function_ref,
setup, headers, end_stream);
}
Http::FilterDataStatus decodeData(Buffer::Instance& data, bool end_stream) override {
return doData(request_stream_wrapper_, data, end_stream);
}
Http::FilterTrailersStatus decodeTrailers(Http::RequestTrailerMap& trailers) override {
return doTrailers(request_stream_wrapper_, trailers);
}
void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override {
decoder_callbacks_.callbacks_ = &callbacks;
}
// Http::StreamEncoderFilter
Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override {
return Http::FilterHeadersStatus::Continue;
}
Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers,
bool end_stream) override {
PerLuaCodeSetup* setup = getPerLuaCodeSetup(config_.get(), decoder_callbacks_.callbacks_);
const int function_ref = setup ? setup->responseFunctionRef() : LUA_REFNIL;
return doHeaders(response_stream_wrapper_, response_coroutine_, encoder_callbacks_,
function_ref, setup, headers, end_stream);
}
Http::FilterDataStatus encodeData(Buffer::Instance& data, bool end_stream) override {
return doData(response_stream_wrapper_, data, end_stream);
};
Http::FilterTrailersStatus encodeTrailers(Http::ResponseTrailerMap& trailers) override {
return doTrailers(response_stream_wrapper_, trailers);
};
Http::FilterMetadataStatus encodeMetadata(Http::MetadataMap&) override {
return Http::FilterMetadataStatus::Continue;
}
void setEncoderFilterCallbacks(Http::StreamEncoderFilterCallbacks& callbacks) override {
encoder_callbacks_.callbacks_ = &callbacks;
};
// …
};
-
PerLuaCodeSetup::PerLuaCodeSetup(const std::string& lua_code, ThreadLocal::SlotAllocator& tls)
-
lua_state_(lua_code, tls) {
// …
// 定义 envoy_on_request 和 envoy_on_response
request_function_slot_ = lua_state_.registerGlobal(“envoy_on_request”, initializers);
if (lua_state_.getGlobalRef(request_function_slot_) == LUA_REFNIL) {
ENVOY_LOG(info, “envoy_on_request() function not found. Lua filter will not hook requests.”);
}
response_function_slot_ = lua_state_.registerGlobal(“envoy_on_response”, initializers);
if (lua_state_.getGlobalRef(response_function_slot_) == LUA_REFNIL) {
ENVOY_LOG(info, “envoy_on_response() function not found. Lua filter will not hook responses.”);
}
}
其他
(1)envoy 架构图
参考 https://mp.weixin.qq.com/s/lrWwvNpgyiyAWtElueG4EA
参考
- envoy 架构与配置结构:https://fuckcloudnative.io/envoy-handbook/docs/gettingstarted/architecture/
- Envoy源码解析
https://zhuanlan.zhihu.com/p/59816311
https://zhuanlan.zhihu.com/p/59816657
https://zhuanlan.zhihu.com/p/59816859 - 过滤链配置:https://skyao.io/learning-xds/docs/lds/api/config-filter-chain.html