Envoy通过LibEvent的event_base_loop函数来处理网络事件,下游连接数据到达时,lievent库通过向assignEvents注册的回调方法来分发事件。 在connection_impl.cc
文件中,事件被分发到ConnectionImpl::onFileEvent回调方法中。先处理关闭事件,然后处理写事件,最后处理读事件。
c++
void ConnectionImpl::onFileEvent(uint32_t events) {
closeSocket(ConnectionEvent::RemoteClose);
return;
}
if (events & Event::FileReadyType::Write) {
onWriteReady();
}
if (ioHandle().isOpen() && (events & Event::FileReadyType::Read)) {
onReadReady();
}
}
读取数据
在connection_impl.cc
文件中,onReadReady方法通过doRead从socket中读取数据,每次最多读取16KB数据放入Buffer中,此处是循环去取数据的,在以下几种情况下会退出循环:
- 读到buffer区空了,正常情况、
- 读够limit大小的数据。默认就是1MB。需要格外关注的是,由于envoy采用了边缘触发,所以如果没有新数据进来,则无法将监听到read事件,这样可能导致数据无法被消费完。为了解决这个问题,所以会通过
callbacks_->setReadBufferReady();
重新触发Read事件。 - 远端关闭连接,即连接被动关闭。
- 发生异常
当读取的数据长度不为0或者读取完成时,进入onRead回调方法。
c++
IoResult result = transport_socket_->doRead(*read_buffer_);
if (result.bytes_processed_ != 0 || result.end_stream_read_ ||
(latched_dispatch_buffered_data && read_buffer_->length() > 0)) {
// Skip onRead if no bytes were processed unless we explicitly want to force onRead for
// buffered data. For instance, skip onRead if the connection was closed without producing
// more data.
onRead(new_buffer_size);
}
在connection_impl.cc
文件的onRead方法,调用了filter_manager_.onRead()。 在filter_manager_impl.cc
文件的onRead方法,调用了onContinueReading方法,该方法在上篇创建连接的时候也用到了,当时是调用相关filter的onNewConnection回调方法。而此处因为已经初始化过了,所以进入每个filter的onData回调方法中。
c++
void FilterManagerImpl::onContinueReading(ActiveReadFilter* filter,
ReadBufferSource& buffer_source) {
for (; entry != upstream_filters_.end(); entry++) {
if (!(*entry)->filter_) {
continue;
}
if (!(*entry)->initialized_) {
... // 回调filter的onNewConnection方法
}
StreamBuffer read_buffer = buffer_source.getReadBuffer();
if (read_buffer.buffer.length() > 0 || read_buffer.end_stream) {
FilterStatus status = (*entry)->filter_->onData(read_buffer.buffer, read_buffer.end_stream);
}
}
}
接收数据
对于一个Sidecar来说,最核心的能力必然就是路由。没有路由,其他的功能都是枉谈。所以这边也就会引出Envoy里面最核心的一个Filter ------ 连接管理器ConnectionManagerImpl
。 在conn_manager_impl.cc
文件中,上述filter的onData回调方法。首先createCodec中根据监听器config配置以及收到的请求头部内容创建解码器codec,看代码此处是针对http的处理(其他协议在哪处理的todo),最终调用到了conn_manager_utility.cc
的determineNextProtocol方法,根据接收到数据的起始内容确实http版本。
c++
std::string ConnectionManagerUtility::determineNextProtocol(Network::Connection& connection,
const Buffer::Instance& data) {
// See if the data we have so far shows the HTTP/2 prefix. We ignore the case where someone sends
// us the first few bytes of the HTTP/2 prefix since in all public cases we use SSL/ALPN. For
// internal cases this should practically never happen.
if (data.startsWith(Http2::CLIENT_MAGIC_PREFIX)) {
return Utility::AlpnNames::get().Http2;
}
return "";
}
然后继续在conn_manager_impl.cc
文件中,执行解码器codec的dispatcher对消息的内容进行处理(就是解码呗)。
从上图可以看到每个协议有单独实现的codec解码器,此处以http1进行分析。 在http1/codec_impl.cc
文件中的dispatch方法中,调用dispatchSlice方法循环处理接收到的数据。
c++
void ConnectionImpl::dispatch(Buffer::Instance& data) {
//......
if (data.length() > 0) {
current_dispatching_buffer_ = &data;
while (data.length() > 0) {
// 每次去data的头部分片slice数据
auto slice = data.frontSlice();
dispatching_slice_already_drained_ = false;
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_);
// 清理已处理过的分片slice数据
data.drain(statusor_parsed.value());
}
total_parsed += statusor_parsed.value();
}
current_dispatching_buffer_ = nullptr;
dispatchBufferedBody();
} else {
auto result = dispatchSlice(nullptr, 0);
if (!result.ok()) {
return result.status();
}
}
}
真实解码的过程即在dispatchSlice中,如下:
c++
Envoy::StatusOr<size_t> ConnectionImpl::dispatchSlice(const char* slice, size_t len) {
ASSERT(codec_status_.ok() && dispatching_);
auto [nread, rc] = parser_->execute(slice, len);
...
return nread;
}
在legacy_parser_impl.cc
文件中,相关execute的具体实现如下:
c++
RcVal execute(const char* slice, int len) {
return {http_parser_execute(&parser_, &settings_, slice, len), HTTP_PARSER_ERRNO(&parser_)};
}
处理数据
此处用到了http_parser库来进行处理。我们继续看下是如何处理的: 在legacy_parser_impl.cc
文件中,http_parse_execute方法会根据当前消息偏移调用http_parser_settings变量中注册的回调方法,包括:on_message_begin、on_url、on_status、on_header_field、on_header_value等等。
c++
settings_ = {
[](http_parser* parser) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onMessageBegin();
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
[](http_parser* parser, const char* at, size_t length) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onUrl(at, length);
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
[](http_parser* parser, const char* at, size_t length) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onStatus(at, length);
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
[](http_parser* parser, const char* at, size_t length) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onHeaderField(at, length);
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
[](http_parser* parser, const char* at, size_t length) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onHeaderValue(at, length);
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
[](http_parser* parser) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto statusor = conn_impl->onHeadersComplete();
return conn_impl->setAndCheckCallbackStatusOr(std::move(statusor));
},
[](http_parser* parser, const char* at, size_t length) -> int {
static_cast<ParserCallbacks*>(parser->data)->bufferBody(at, length);
return 0;
},
[](http_parser* parser) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onMessageComplete();
return conn_impl->setAndCheckCallbackStatusOr(std::move(status));
},
[](http_parser* parser) -> int {
// A 0-byte chunk header is used to signal the end of the chunked body.
// When this function is called, http-parser holds the size of the chunk in
// parser->content_length. See
// https://github.com/nodejs/http-parser/blob/v2.9.3/http_parser.h#L336
const bool is_final_chunk = (parser->content_length == 0);
static_cast<ParserCallbacks*>(parser->data)->onChunkHeader(is_final_chunk);
return 0;
},
nullptr // on_chunk_complete
}
onMessageBegin环节
设置一个Codec(ServerConnection)的Decoder和Encoder。这边的Encoder即为ServerConnection自己(注意,ServerConnection持有了网络层的ConnectionImpl实例,可以用以进行响应回写,后面会进一步提及),Decoder即为ActiveStream。ActiveStream会持有ServerConnection(有点绕)。 在codec_impl.cc
文件的ServerConnectionImpl::onMessageBeginBase()
c++
Status ServerConnectionImpl::onMessageBeginBase() {
active_request_ = std::make_unique<ActiveRequest>(*this, std::move(bytes_meter_before_stream_));
active_request_->request_decoder_ = &callbacks_.newStream(active_request_->response_encoder_);
}
总而言之,我们记住一点,在这个阶段,会完成Downstream请求的Decoder和Encoder的初始化,并且提前塞好各种回调信息。便于在各个环节被使用。