0
点赞
收藏
分享

微信扫一扫

不懂envoy源码也敢说精通istio之-envoy初始化-(二)

小安子啊 2023-06-06 阅读 102

热重启

source/server/hot_restart_impl.cc 121行

void HotRestartImpl::initialize(Event::Dispatcher& dispatcher, Server::Instance& server) {//热重启初始化
as_parent_.initialize(dispatcher, server);//父亲初始化
}

source/server/hot_restarting_parent.cc 24行

void HotRestartingParent::initialize(Event::Dispatcher& dispatcher, Server::Instance& server) {//父亲初始化
socket_event_ = dispatcher.createFileEvent(
myDomainSocket(),
[this](uint32_t events) -> void {
ASSERT(events == Event::FileReadyType::Read);
onSocketEvent();
},
Event::FileTriggerType::Edge, Event::FileReadyType::Read);
internal_ = std::make_unique<Internal>(
}

source/common/event/dispatcher_impl.cc 177行

FileEventPtr DispatcherImpl::createFileEvent(os_fd_t fd, FileReadyCb cb, FileTriggerType trigger,
uint32_t events)
{
ASSERT(isThreadSafe());
return FileEventPtr{new FileEventImpl(
*this, fd,
[this, cb](uint32_t events) {
touchWatchdog();
cb(events);
},
trigger, events)};
}

source/common/event/file_event_impl.cc 13行

FileEventImpl::FileEventImpl(DispatcherImpl& dispatcher, os_fd_t fd, FileReadyCb cb,
FileTriggerType trigger, uint32_t events)
: dispatcher_(dispatcher), cb_(cb), fd_(fd), trigger_(trigger), enabled_events_(events),
activation_cb_(dispatcher.createSchedulableCallback([this]() {
ASSERT(injected_activation_events_ != 0);
mergeInjectedEventsAndRunCb(0);
})) {
// Treat the lack of a valid fd (which in practice should only happen if we run out of FDs) as
// an OOM condition and just crash.
RELEASE_ASSERT(SOCKET_VALID(fd), );
#ifdef WIN32
ASSERT(trigger_ != FileTriggerType::Edge, libevent does not support edge triggers on Windows);
#endif
if constexpr (PlatformDefaultTriggerType != FileTriggerType::EmulatedEdge) {
ASSERT(trigger_ != FileTriggerType::EmulatedEdge,
Cannot use EmulatedEdge events if they are not the default platform type);
}

assignEvents(events, &dispatcher.base());
event_add(&raw_event_, nullptr);
}

source/common/event/file_event_impl.cc 55行

void FileEventImpl::assignEvents(uint32_t events, event_base* base) {
ASSERT(dispatcher_.isThreadSafe());
ASSERT(base != nullptr);

enabled_events_ = events;
event_assign(
&raw_event_, base, fd_,
EV_PERSIST | (trigger_ == FileTriggerType::Edge ? EV_ET : 0) |
(events & FileReadyType::Read ? EV_READ : 0) |
(events & FileReadyType::Write ? EV_WRITE : 0) |
(events & FileReadyType::Closed ? EV_CLOSED : 0),
[](evutil_socket_t, short what, void* arg) -> void {
auto* event = static_cast<FileEventImpl*>(arg);
uint32_t events = 0;
if (what & EV_READ) {
events |= FileReadyType::Read;
}

if (what & EV_WRITE) {
events |= FileReadyType::Write;
}

if (what & EV_CLOSED) {
events |= FileReadyType::Closed;
}

ASSERT(events != 0);
event->mergeInjectedEventsAndRunCb(events);
},
this);
}

source/server/hot_restarting_parent.cc 88行

HotRestartingParent::Internal::Internal(Server::Instance* server) : server_(server) {
Stats::Gauge& hot_restart_generation = hotRestartGeneration(server->stats());
hot_restart_generation.inc();
}

source/server/hot_restarting_base.cc 263行

Stats::Gauge& HotRestartingBase::hotRestartGeneration(Stats::Scope& scope) {
// Track the hot-restart generation. Using gauge's accumulate semantics,
// the increments will be combined across hot-restart. This may be useful
// at some point, though the main motivation for this stat is to enable
// an integration test showing that dynamic stat-names can be coalesced
// across hot-restarts. There's no other reason this particular stat-name
// needs to be created dynamically.
//
// Note also, this stat cannot currently be represented as a counter due to
// the way stats get latched on sink update. See the comment in
// InstanceUtil::flushMetricsToSinks.
return Stats::Utility::gaugeFromElements(scope,
{Stats::DynamicName(server.hot_restart_generation)},
Stats::Gauge::ImportMode::Accumulate);
}

初始化

source/exec/main_common.cc 32行

Server::DrainManagerPtr ProdComponentFactory::createDrainManager(Server::Instance& server) {//创建drain管理器
// The global drain manager only triggers on listener modification, which effectively is
// hot restart at the global level. The per-listener drain managers decide whether to
// to include /healthcheck/fail status.
return std::make_unique<Server::DrainManagerImpl>(
server, envoy::config::listener::v3::Listener::MODIFY_ONLY, server.dispatcher());
}

source/server/server.cc 387行

void InstanceImpl::initialize(Network::Address::InstanceConstSharedPtr local_address,
ComponentFactory& component_factory) {//初始化
ENVOY_LOG(info, initializing epoch {} (base id={}, hot restart version={}),
options_.restartEpoch(), restarter_.baseId(), restarter_.version());//记录日志

ENVOY_LOG(info, statically linked extensions:);
for (const auto& ext : Envoy::Registry::FactoryCategoryRegistry::registeredFactories()) {//遍历注册的工厂
ENVOY_LOG(info, {}: {}, ext.first, absl::StrJoin(ext.second->registeredNames(), , ));//记录日志
}

// Handle configuration that needs to take place prior to the main configuration load.
InstanceUtil::loadBootstrapConfig(bootstrap_, options_,
messageValidationContext().staticValidationVisitor(), *api_);//加载启动配置
bootstrap_config_update_time_ = time_source_.systemTime();//设置配置更新时间

#ifdef ENVOY_PERFETTO //如果定义了ENVOY_PERFETTO
perfetto::TracingInitArgs args;
// Include in-process events only.
args.backends = perfetto::kInProcessBackend;
perfetto::Tracing::Initialize(args);//初始化性能追踪
perfetto::TrackEvent::Register();//注册事件

// Prepare a configuration for a new Perfetto tracing session.
perfetto::TraceConfig cfg;
// TODO(rojkov): make the tracer configurable with either Perfetto's native
// message or custom one embedded into Bootstrap.
cfg.add_buffers()->set_size_kb(1024);//缓存大小配置
auto* ds_cfg = cfg.add_data_sources()->mutable_config();//设置数据源
ds_cfg->set_name(track_event);//设置数据源名称

const std::string pftrace_path =
PROTOBUF_GET_STRING_OR_DEFAULT(bootstrap_, perf_tracing_file_path, envoy.pftrace);//获取记录性能分析文件路径
// Instantiate a new tracing session.
tracing_session_ = perfetto::Tracing::NewTrace();//创建trace
tracing_fd_ = open(pftrace_path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);//打开文件
if (tracing_fd_ == -1) {//如果文件描述符是-1,则报异常
throw EnvoyException(
fmt::format(unable to open tracing file {}: {}, pftrace_path, errorDetails(errno)));
}
// Configure the tracing session.
tracing_session_->Setup(cfg, tracing_fd_);//初始化
// Enable tracing and block until tracing has started.
tracing_session_->StartBlocking();//启动性能分析
#endif

// Immediate after the bootstrap has been loaded, override the header prefix, if configured to
// do so. This must be set before any other code block references the HeaderValues ConstSingleton.
if (!bootstrap_.header_prefix().empty()) {//如果启动配置中头前不为空
// setPrefix has a release assert verifying that setPrefix() is not called after prefix()
ThreadSafeSingleton<Http::PrefixValue>::get().setPrefix(bootstrap_.header_prefix().c_str());//设置http头前缀
}

// Register Custom O(1) headers from bootstrap.
registerCustomInlineHeadersFromBootstrap(bootstrap_);//注册自定义inline头

ENVOY_LOG(info, HTTP header map info:);
for (const auto& info : Http::HeaderMapImplUtility::getAllHeaderMapImplInfo()) {//获取所有头信息,并遍历
ENVOY_LOG(info, {}: {} bytes: {}, info.name_, info.size_,
absl::StrJoin(info.registered_headers_, ,));//打印头信息
}

// Initialize the regex engine and inject to singleton.
// Needs to happen before stats store initialization because the stats
// matcher config can include regexes.
if (bootstrap_.has_default_regex_engine()) {//如果启动配置中有默认正则引擎配置
const auto& default_regex_engine = bootstrap_.default_regex_engine();//获取默认正则引擎
Regex::EngineFactory& factory =
Config::Utility::getAndCheckFactory<Regex::EngineFactory>(default_regex_engine);//获取正则引擎工厂
auto config = Config::Utility::translateAnyToFactoryConfig(
default_regex_engine.typed_config(), messageValidationContext().staticValidationVisitor(),
factory);//获取正则引擎配置信息
regex_engine_ = factory.createEngine(*config, serverFactoryContext());//创建正则引擎
} else {
regex_engine_ = std::make_shared<Regex::GoogleReEngine>();//创建google正则引擎
}
Regex::EngineSingleton::clear();//清理引擎单例
Regex::EngineSingleton::initialize(regex_engine_.get());//初始化引擎单例

// Needs to happen as early as possible in the instantiation to preempt the objects that require
// stats.
stats_store_.setTagProducer(Config::Utility::createTagProducer(bootstrap_, options_.statsTags()));//设置标签生产者
stats_store_.setStatsMatcher(
Config::Utility::createStatsMatcher(bootstrap_, stats_store_.symbolTable()));//设置统计matcher
stats_store_.setHistogramSettings(Config::Utility::createHistogramSettings(bootstrap_));//设置Histogram设置

const std::string server_stats_prefix = server.;//server统计前缀
const std::string server_compilation_settings_stats_prefix = server.compilation_settings;//服务器编译设置统计前缀
server_stats_ = std::make_unique<ServerStats>(
ServerStats{ALL_SERVER_STATS(POOL_COUNTER_PREFIX(stats_store_, server_stats_prefix),
POOL_GAUGE_PREFIX(stats_store_, server_stats_prefix),
POOL_HISTOGRAM_PREFIX(stats_store_, server_stats_prefix))});//创建服务器统计
server_compilation_settings_stats_ =
std::make_unique<CompilationSettings::ServerCompilationSettingsStats>(
CompilationSettings::ServerCompilationSettingsStats{ALL_SERVER_COMPILATION_SETTINGS_STATS(
POOL_COUNTER_PREFIX(stats_store_, server_compilation_settings_stats_prefix),
POOL_GAUGE_PREFIX(stats_store_, server_compilation_settings_stats_prefix),
POOL_HISTOGRAM_PREFIX(stats_store_, server_compilation_settings_stats_prefix))});//创建服务器编译设置统计
validation_context_.setCounters(server_stats_->static_unknown_fields_,
server_stats_->dynamic_unknown_fields_,
server_stats_->wip_protos_);//校验上下文设置计数

initialization_timer_ = std::make_unique<Stats::HistogramCompletableTimespanImpl>(
server_stats_->initialization_time_ms_, timeSource());//创建HistogramCompletableTimespanImpl
server_stats_->concurrency_.set(options_.concurrency());//服务统计设置并发
server_stats_->hot_restart_epoch_.set(options_.restartEpoch());//服务统计设置热启动纪元
InstanceImpl::failHealthcheck(false);//服务器实例设置失败健康检查

// Check if bootstrap has server version override set, if yes, we should use that as
// 'server.version' stat.
uint64_t version_int;
if (bootstrap_.stats_server_version_override().value() > 0) {//如果设置了统计服务器版本重写
version_int = bootstrap_.stats_server_version_override().value();//获取版本
} else {
if (!StringUtil::atoull(VersionInfo::revision().substr(0, 6).c_str(), version_int, 16)) {//设置版本
throw EnvoyException(compiled GIT SHA is invalid. Invalid build.);
}
}
server_stats_->version_.set(version_int);//设置服务器统计版本
if (VersionInfo::sslFipsCompliant()) {//如果设置了sslFipsCompliant
server_compilation_settings_stats_->fips_mode_.set(1);//设置flips mode
}

// If user has set user_agent_name in the bootstrap config, use it.
// Default to envoy if unset.
if (bootstrap_.node().user_agent_name().empty()) {//如果启动信息的用户代理名称为空
bootstrap_.mutable_node()->set_user_agent_name(envoy);//设置用户代理名称为envoy
}

// If user has set user_agent_build_version in the bootstrap config, use it.
// Default to the internal server version.
if (!bootstrap_.node().user_agent_build_version().has_version()) {//如果启动信息的node里没有版本
*bootstrap_.mutable_node()->mutable_user_agent_build_version() = VersionInfo::buildVersion();//设置启动信息node版本
}

for (const auto& ext : Envoy::Registry::FactoryCategoryRegistry::registeredFactories()) {//遍历注册的工厂
auto registered_types = ext.second->registeredTypes();//注册类型
for (const auto& name : ext.second->allRegisteredNames()) {//遍历注册名称
auto* extension = bootstrap_.mutable_node()->add_extensions();//启动配置节点添加扩展
extension->set_name(std::string(name));//设置扩展名称
extension->set_category(ext.first);//设置扩展类别
auto const version = ext.second->getFactoryVersion(name);//获取工厂版本
if (version) {
*extension->mutable_version() = version.value();//设置扩展版本
}
extension->set_disabled(ext.second->isFactoryDisabled(name));//设置禁用
auto it = registered_types.find(name);//根据名称查找注册类型
if (it != registered_types.end()) {//如果找到注册类型
std::sort(it->second.begin(), it->second.end());//排序
for (const auto& type_url : it->second) {//遍历类型url
extension->add_type_urls(type_url);//扩展添加类型url
}
}
}
}

local_info_ = std::make_unique<LocalInfo::LocalInfoImpl>(//创建本地信息实现
stats().symbolTable(), bootstrap_.node(), bootstrap_.node_context_params(), local_address,
options_.serviceZone(), options_.serviceClusterName(), options_.serviceNodeName());

Configuration::InitialImpl initial_config(bootstrap_);//创建初始化实现

// Learn original_start_time_ if our parent is still around to inform us of it.
const auto parent_admin_shutdown_response = restarter_.sendParentAdminShutdownRequest();//热重启发送父亲admin关闭请求
if (parent_admin_shutdown_response.has_value()) {//如果父亲admin关闭响应有值
original_start_time_ = parent_admin_shutdown_response.value().original_start_time_;//获取原始启动时间
// TODO(soulxu): This is added for switching the reuse port default value as true (#17259).
// It ensures the same default value during the hot restart. This can be removed when
// everyone switches to the new default value.
enable_reuse_port_default_ =
parent_admin_shutdown_response.value().enable_reuse_port_default_ ? true : false;//设置是否默认重用端口
}
admin_ = std::make_unique<AdminImpl>(initial_config.admin().profilePath(), *this,
initial_config.admin().ignoreGlobalConnLimit());//创建admin实现

loadServerFlags(initial_config.flagsPath());

secret_manager_ = std::make_unique<Secret::SecretManagerImpl>(admin_->getConfigTracker());

// Initialize the overload manager early so other modules can register for actions.
overload_manager_ = std::make_unique<OverloadManagerImpl>(
*dispatcher_, stats_store_, thread_local_, bootstrap_.overload_manager(),
messageValidationContext().staticValidationVisitor(), *api_, options_);

heap_shrinker_ =
std::make_unique<Memory::HeapShrinker>(*dispatcher_, *overload_manager_, stats_store_);

for (const auto& bootstrap_extension : bootstrap_.bootstrap_extensions()) {
auto& factory = Config::Utility::getAndCheckFactory<Configuration::BootstrapExtensionFactory>(
bootstrap_extension);
auto config = Config::Utility::translateAnyToFactoryConfig(
bootstrap_extension.typed_config(), messageValidationContext().staticValidationVisitor(),
factory);
bootstrap_extensions_.push_back(
factory.createBootstrapExtension(*config, serverFactoryContext()));
}

// Register the fatal actions.
{
FatalAction::FatalActionPtrList safe_actions;
FatalAction::FatalActionPtrList unsafe_actions;
for (const auto& action_config : bootstrap_.fatal_actions()) {
auto& factory =
Config::Utility::getAndCheckFactory<Server::Configuration::FatalActionFactory>(
action_config.config());
auto action = factory.createFatalActionFromProto(action_config, this);

if (action->isAsyncSignalSafe()) {
safe_actions.push_back(std::move(action));
} else {
unsafe_actions.push_back(std::move(action));
}
}
Envoy::FatalErrorHandler::registerFatalActions(
std::move(safe_actions), std::move(unsafe_actions), api_->threadFactory());
}

if (!bootstrap_.default_socket_interface().empty()) {
auto& sock_name = bootstrap_.default_socket_interface();
auto sock = const_cast<Network::SocketInterface*>(Network::socketInterface(sock_name));
if (sock != nullptr) {
Network::SocketInterfaceSingleton::clear();
Network::SocketInterfaceSingleton::initialize(sock);
}
}

// Workers get created first so they register for thread local updates.
listener_manager_ =
std::make_unique<ListenerManagerImpl>(*this, listener_component_factory_, worker_factory_,
bootstrap_.enable_dispatcher_stats(), quic_stat_names_);

// The main thread is also registered for thread local updates so that code that does not care
// whether it runs on the main thread or on workers can still use TLS.
thread_local_.registerThread(*dispatcher_, true);

// We can now initialize stats for threading.
stats_store_.initializeThreading(*dispatcher_, thread_local_);

// It's now safe to start writing stats from the main thread's dispatcher.
if (bootstrap_.enable_dispatcher_stats()) {
dispatcher_->initializeStats(stats_store_, server.);
}

// The broad order of initialization from this point on is the following:
// 1. Statically provisioned configuration (bootstrap) are loaded.
// 2. Cluster manager is created and all primary clusters (i.e. with endpoint assignments
// provisioned statically in bootstrap, discovered through DNS or file based CDS) are
// initialized.
// 3. Various services are initialized and configured using the bootstrap config.
// 4. RTDS is initialized using primary clusters. This allows runtime overrides to be fully
// configured before the rest of xDS configuration is provisioned.
// 5. Secondary clusters (with endpoint assignments provisioned by xDS servers) are initialized.
// 6. The rest of the dynamic configuration is provisioned.
//
// Please note: this order requires that RTDS is provisioned using a primary cluster. If RTDS is
// provisioned through ADS then ADS must use primary cluster as well. This invariant is enforced
// during RTDS initialization and invalid configuration will be rejected.

// Runtime gets initialized before the main configuration since during main configuration
// load things may grab a reference to the loader for later use.
Runtime::LoaderPtr runtime_ptr = component_factory.createRuntime(*this, initial_config);
if (runtime_ptr->snapshot().getBoolean(envoy.restart_features.remove_runtime_singleton, true)) {
runtime_ = std::move(runtime_ptr);
} else {
runtime_singleton_ = std::make_unique<Runtime::ScopedLoaderSingleton>(std::move(runtime_ptr));
}
initial_config.initAdminAccessLog(bootstrap_, *this);
validation_context_.setRuntime(runtime());

if (!runtime().snapshot().getBoolean(envoy.disallow_global_stats, false)) {
assert_action_registration_ = Assert::addDebugAssertionFailureRecordAction(
[this](const char*) { server_stats_->debug_assertion_failures_.inc(); });
envoy_bug_action_registration_ = Assert::addEnvoyBugFailureRecordAction(
[this](const char*) { server_stats_->envoy_bug_failures_.inc(); });
}

if (initial_config.admin().address()) {
admin_->startHttpListener(initial_config.admin().accessLogs(), options_.adminAddressPath(),
initial_config.admin().address(),
initial_config.admin().socketOptions(),
stats_store_.createScope(listener.admin.));
} else {
ENVOY_LOG(warn, No admin address given, so no admin HTTP server started.);
}
config_tracker_entry_ = admin_->getConfigTracker().add(
bootstrap, [this](const Matchers::StringMatcher&) { return dumpBootstrapConfig(); });
if (initial_config.admin().address()) {
admin_->addListenerToHandler(handler_.get());
}

// Once we have runtime we can initialize the SSL context manager.
ssl_context_manager_ = createContextManager(ssl_context_manager, time_source_);

envoy::config::core::v3::TypedExtensionConfig typed_dns_resolver_config;
Network::DnsResolverFactory& dns_resolver_factory =
Network::createDnsResolverFactoryFromProto(bootstrap_, typed_dns_resolver_config);
dns_resolver_ =
dns_resolver_factory.createDnsResolver(dispatcher(), api(), typed_dns_resolver_config);

cluster_manager_factory_ = std::make_unique<Upstream::ProdClusterManagerFactory>(
*admin_, runtime(), stats_store_, thread_local_, dns_resolver_, *ssl_context_manager_,
*dispatcher_, *local_info_, *secret_manager_, messageValidationContext(), *api_,
http_context_, grpc_context_, router_context_, access_log_manager_, *singleton_manager_,
options_, quic_stat_names_, *this);

// Now the configuration gets parsed. The configuration may start setting
// thread local data per above. See MainImpl::initialize() for why ConfigImpl
// is constructed as part of the InstanceImpl and then populated once
// cluster_manager_factory_ is available.
config_.initialize(bootstrap_, *this, *cluster_manager_factory_);

// Instruct the listener manager to create the LDS provider if needed. This must be done later
// because various items do not yet exist when the listener manager is created.
if (bootstrap_.dynamic_resources().has_lds_config() ||
!bootstrap_.dynamic_resources().lds_resources_locator().empty()) {
std::unique_ptr<xds::core::v3::ResourceLocator> lds_resources_locator;
if (!bootstrap_.dynamic_resources().lds_resources_locator().empty()) {
lds_resources_locator =
std::make_unique<xds::core::v3::ResourceLocator>(Config::XdsResourceIdentifier::decodeUrl(
bootstrap_.dynamic_resources().lds_resources_locator()));
}
listener_manager_->createLdsApi(bootstrap_.dynamic_resources().lds_config(),
lds_resources_locator.get());
}

// We have to defer RTDS initialization until after the cluster manager is
// instantiated (which in turn relies on runtime...).
runtime().initialize(clusterManager());

clusterManager().setPrimaryClustersInitializedCb(
[this]() { onClusterManagerPrimaryInitializationComplete(); });

auto& stats_config = config_.statsConfig();
for (const Stats::SinkPtr& sink : stats_config.sinks()) {
stats_store_.addSink(*sink);
}
if (!stats_config.flushOnAdmin()) {
// Some of the stat sinks may need dispatcher support so don't flush until the main loop starts.
// Just setup the timer.
stat_flush_timer_ = dispatcher_->createTimer([this]() -> void { flushStats(); });
stat_flush_timer_->enableTimer(stats_config.flushInterval());
}

// Now that we are initialized, notify the bootstrap extensions.
for (auto&& bootstrap_extension : bootstrap_extensions_) {
bootstrap_extension->onServerInitialized();
}

// GuardDog (deadlock detection) object and thread setup before workers are
// started and before our own run() loop runs.
main_thread_guard_dog_ = std::make_unique<Server::GuardDogImpl>(
stats_store_, config_.mainThreadWatchdogConfig(), *api_, main_thread);
worker_guard_dog_ = std::make_unique<Server::GuardDogImpl>(
stats_store_, config_.workerWatchdogConfig(), *api_, workers);
}

envoy/common/protobuf/utility.h 41行

#define PROTOBUF_GET_STRING_OR_DEFAULT(message, field_name, default_value)                         \
(!(message).field_name().empty() ? (message).field_name() : (default_value))

加载配置

source/server/server.cc 358行

void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& bootstrap,
const Options& options,
ProtobufMessage::ValidationVisitor& validation_visitor,
Api::Api& api) {
const std::string& config_path = options.configPath();//获取json配置路径
const std::string& config_yaml = options.configYaml();//获取yaml配置路径
const envoy::config::bootstrap::v3::Bootstrap& config_proto = options.configProto();//获取proto配置

// Exactly one of config_path and config_yaml should be specified.
if (config_path.empty() && config_yaml.empty() && config_proto.ByteSizeLong() == 0) {//如果配置为空则报异常
throw EnvoyException(At least one of --config-path or --config-yaml or Options::configProto()
should be non-empty);
}

if (!config_path.empty()) {//如果json配置不为空
MessageUtil::loadFromFile(config_path, bootstrap, validation_visitor, api);//加载配置
}
if (!config_yaml.empty()) {//如果yaml配置不为空
envoy::config::bootstrap::v3::Bootstrap bootstrap_override;
MessageUtil::loadFromYaml(config_yaml, bootstrap_override, validation_visitor);//加载yaml配置
// TODO(snowp): The fact that we do a merge here doesn't seem to be covered under test.
bootstrap.MergeFrom(bootstrap_override);//合并json配置和yaml配置
}
if (config_proto.ByteSizeLong() != 0) {//如果proto配置不为空
bootstrap.MergeFrom(config_proto);//合并proto配置
}
MessageUtil::validate(bootstrap, validation_visitor);//校验配置
}

source/common/protobuf/utility.cc 305行

void MessageUtil::loadFromFile(const std::string& path, Protobuf::Message& message,
ProtobufMessage::ValidationVisitor& validation_visitor,
Api::Api& api) {//加载json配置
const std::string contents = api.fileSystem().fileReadToEnd(path);//读取文件内容
// If the filename ends with .pb, attempt to parse it as a binary proto.
if (absl::EndsWithIgnoreCase(path, FileExtensions::get().ProtoBinary)) {//如果配置文件是以.pb结尾
// Attempt to parse the binary format.
if (message.ParseFromString(contents)) {//解析配置
MessageUtil::checkForUnexpectedFields(message, validation_visitor);//检查不允许的字段
}
return;
}

// If the filename ends with .pb_text, attempt to parse it as a text proto.
if (absl::EndsWithIgnoreCase(path, FileExtensions::get().ProtoText)) {//如果文件是以.pb_text结尾
if (Protobuf::TextFormat::ParseFromString(contents, &message)) {//解析配置
return;
}
throw EnvoyException(Unable to parse file \ + path + \ as a text protobuf (type +
message.GetTypeName() + ));
}
if (absl::EndsWithIgnoreCase(path, FileExtensions::get().Yaml) ||
absl::EndsWithIgnoreCase(path, FileExtensions::get().Yml)) {//如果文件是以.yaml或.yml结尾
loadFromYaml(contents, message, validation_visitor);//以yaml方式加载配置
} else {
loadFromJson(contents, message, validation_visitor);//以json方式加载配置
}
}

source/common/filesystem/posix/filesystem_impl.cc 118行

std::string InstanceImplPosix::fileReadToEnd(const std::string& path) {//读取文件到末尾
if (illegalPath(path)) {//如果是非法路径
throw EnvoyException(absl::StrCat(Invalid path: , path));
}

std::ifstream file(path);//创建输入文件流
if (file.fail()) {//如果打开文件失败
throw EnvoyException(absl::StrCat(unable to read file: , path));
}

std::stringstream file_string;
file_string << file.rdbuf();//读取文件

return file_string.str();//范围字符串
}

source/common/filesystem/posix/filesystem_impl.cc 179行

Api::SysCallStringResult InstanceImplPosix::canonicalPath(const std::string& path) {//获取路径的经典路径
char* resolved_path = ::realpath(path.c_str(), nullptr);//获取真实路径
if (resolved_path == nullptr) {//如果路径为空
return {std::string(), errno};
}
std::string resolved_path_string{resolved_path};
::free(resolved_path);//释放原字符串
return {resolved_path_string, 0};//返回路径
}

source/common/filesystem/posix/filesystem_impl.cc 147行

bool InstanceImplPosix::illegalPath(const std::string& path) {//判断路径是否合法
// Special case, allow /dev/fd/* access here so that config can be passed in a
// file descriptor from a bootstrap script via exec. The reason we do this
// _before_ canonicalizing the path is that different unix flavors implement
// /dev/fd/* differently, for example on linux they are symlinks to /dev/pts/*
// which are symlinks to /proc/self/fds/. On BSD (and darwin) they are not
// symlinks at all. To avoid lots of platform, specifics, we allowlist
// /dev/fd/* _before_ resolving the canonical path.
if (absl::StartsWith(path, /dev/fd/)) {//如果路径以/dev/fd/开始,则合法
return false;
}

const Api::SysCallStringResult canonical_path = canonicalPath(path);//获取真实路径
if (canonical_path.return_value_.empty()) {//如果返回值是空,则非法
ENVOY_LOG_MISC(debug, Unable to determine canonical path for {}: {}, path,
errorDetails(canonical_path.errno_));
return true;
}

// Platform specific path sanity; we provide a convenience to avoid Envoy
// instances poking in bad places. We may have to consider conditioning on
// platform in the future, growing these or relaxing some constraints (e.g.
// there are valid reasons to go via /proc for file paths).
// TODO(htuch): Optimize this as a hash lookup if we grow any further.
if (absl::StartsWith(canonical_path.return_value_, /dev) ||
absl::StartsWith(canonical_path.return_value_, /sys) ||
absl::StartsWith(canonical_path.return_value_, /proc)) {//如果经典路径以/dev,/sys,/proc开头则非法
return true;
}
return false;//合法
}

envoy/common/protobuf/utility.h 232行

  class FileExtensionValues {//文件扩展末尾
public:
const std::string ProtoBinary = .pb;
const std::string ProtoBinaryLengthDelimited = .pb_length_delimited;
const std::string ProtoText = .pb_text;
const std::string Json = .json;
const std::string Yaml = .yaml;
const std::string Yml = .yml;
};

using FileExtensions = ConstSingleton<FileExtensionValues>;//常单例

envoy/common/protobuf/utility.h 294行

void MessageUtil::loadFromYaml(const std::string& yaml, Protobuf::Message& message,
ProtobufMessage::ValidationVisitor& validation_visitor) {//从yaml加载配置
ProtobufWkt::Value value = ValueUtil::loadFromYaml(yaml);//加载yaml为ProtobufWkt::Value
if (value.kind_case() == ProtobufWkt::Value::kStructValue ||
value.kind_case() == ProtobufWkt::Value::kListValue) {
jsonConvertInternal(value, validation_visitor, message);//转换为json,再转换为protobuf
return;
}
throw EnvoyException(Unable to convert YAML as JSON: + yaml);
}

envoy/common/protobuf/utility.h 789行

ProtobufWkt::Value ValueUtil::loadFromYaml(const std::string& yaml) {//从yaml加载
TRY_ASSERT_MAIN_THREAD { return parseYamlNode(YAML::Load(yaml)); }//加载yaml,解析yaml节点
END_TRY
catch (YAML::ParserException& e) {
throw EnvoyException(e.what());
}
catch (YAML::BadConversion& e) {
throw EnvoyException(e.what());
}
catch (std::exception& e) {
// There is a potentially wide space of exceptions thrown by the YAML parser,
// and enumerating them all may be difficult. Envoy doesn't work well with
// unhandled exceptions, so we capture them and record the exception name in
// the Envoy Exception text.
throw EnvoyException(fmt::format(Unexpected YAML exception: {}, +e.what()));
}
}

envoy/common/protobuf/utility.h 53行

ProtobufWkt::Value parseYamlNode(const YAML::Node& node) {//解析yaml节点
ProtobufWkt::Value value;
switch (node.Type()) {
case YAML::NodeType::Null://如果节点类型是null
value.set_null_value(ProtobufWkt::NULL_VALUE);//设置null值
break;
case YAML::NodeType::Scalar: {//如果节点类型是Scalar
if (node.Tag() == !) {//如果tag是!
value.set_string_value(node.as<std::string>());//设置value
break;
}
bool bool_value;
if (YAML::convert<bool>::decode(node, bool_value)) {//如果节点是bool值
value.set_bool_value(bool_value);//设置bool值
break;
}
int64_t int_value;
if (YAML::convert<int64_t>::decode(node, int_value)) {//如果节点是整型值
if (std::numeric_limits<int32_t>::min() <= int_value &&
std::numeric_limits<int32_t>::max() >= int_value) {//如果整型值在范围以内
// We could convert all integer values to string but it will break some stuff relying on
// ProtobufWkt::Struct itself, only convert small numbers into number_value here.
value.set_number_value(int_value);//设置整型值
} else {
// Proto3 JSON mapping allows use string for integer, this still has to be converted from
// int_value to support hexadecimal and octal literals.
value.set_string_value(std::to_string(int_value));//设置字符串值
}
break;
}
// Fall back on string, including float/double case. When protobuf parse the JSON into a message
// it will convert based on the type in the message definition.
value.set_string_value(node.as<std::string>());//设置字符串值
break;
}
case YAML::NodeType::Sequence: {//如果node类型是Sequence
auto& list_values = *value.mutable_list_value()->mutable_values();
for (const auto& it : node) {//遍历node
*list_values.Add() = parseYamlNode(it);//递归调用
}
break;
}
case YAML::NodeType::Map: {//如果节点类型是map
auto& struct_fields = *value.mutable_struct_value()->mutable_fields();
for (const auto& it : node) {//遍历node
if (it.first.Tag() != !ignore) {//如果first tag不是!ignore
struct_fields[it.first.as<std::string>()] = parseYamlNode(it.second);//递归调用
}
}
break;
}
case YAML::NodeType::Undefined://如果node类型是Undefined,抛异常
throw EnvoyException(Undefined YAML value);
}
return value;
}

envoy/common/protobuf/utility.h 110行

void jsonConvertInternal(const Protobuf::Message& source,
ProtobufMessage::ValidationVisitor& validation_visitor,
Protobuf::Message& dest) {//json内部转换
Protobuf::util::JsonPrintOptions json_options;
json_options.preserve_proto_field_names = true;
std::string json;
const auto status = Protobuf::util::MessageToJsonString(source, &json, json_options);//将消息转换为json
if (!status.ok()) {
throw EnvoyException(fmt::format(Unable to convert protobuf message to JSON string: {} {},
status.ToString(), source.DebugString()));
}
MessageUtil::loadFromJson(json, dest, validation_visitor);//将json转换为消息
}

envoy/common/protobuf/utility.h 241行

void MessageUtil::loadFromJson(const std::string& json, Protobuf::Message& message,
ProtobufMessage::ValidationVisitor& validation_visitor) {//从josn加载配置
bool has_unknown_field;
auto status = loadFromJsonNoThrow(json, message, has_unknown_field);//json转换为message
if (status.ok()) {//如果解析成功,则返回
return;
}
if (has_unknown_field) {//如果有未知字段
// If the parsing failure is caused by the unknown fields.
validation_visitor.onUnknownField(type + message.GetTypeName() + reason +
status.ToString());
} else {
// If the error has nothing to do with unknown field.
throw EnvoyException(Unable to parse JSON as proto ( + status.ToString() + ): + json);
}
}

envoy/common/protobuf/utility.h 258行

Protobuf::util::Status MessageUtil::loadFromJsonNoThrow(const std::string& json,
Protobuf::Message& message,
bool& has_unknown_fileld) {//从接送加载,不抛异常
has_unknown_fileld = false;
Protobuf::util::JsonParseOptions options;//解析选项
options.case_insensitive_enum_parsing = true;
// Let's first try and get a clean parse when checking for unknown fields;
// this should be the common case.
options.ignore_unknown_fields = false;
const auto strict_status = Protobuf::util::JsonStringToMessage(json, &message, options);//把json转换成message
if (strict_status.ok()) {//如狗转换成功,则返回
// Success, no need to do any extra work.
return strict_status;
}
// If we fail, we see if we get a clean parse when allowing unknown fields.
// This is essentially a workaround
// for https://github.com/protocolbuffers/protobuf/issues/5967.
// TODO(htuch): clean this up when protobuf supports JSON/YAML unknown field
// detection directly.
options.ignore_unknown_fields = true;//忽略大小写
const auto relaxed_status = Protobuf::util::JsonStringToMessage(json, &message, options);//把接送转换成message
// If we still fail with relaxed unknown field checking, the error has nothing
// to do with unknown fields.
if (relaxed_status.ok()) {//转换成功,返回
has_unknown_fileld = true;//有未知字设为true
return strict_status;
}
return relaxed_status;
}

envoy/common/protobuf/utility.h 298行

  template <class MessageType>
static void validate(const MessageType& message,
ProtobufMessage::ValidationVisitor& validation_visitor,
bool recurse_into_any = false)
{//校验message
// Log warnings or throw errors if deprecated fields or unknown fields are in use.
if (!validation_visitor.skipValidation()) {//如果不跳过校验
checkForUnexpectedFields(message, validation_visitor, recurse_into_any);//检查不期望的字段
}

// TODO(mattklein123): This will recurse the message twice, once above and once for PGV. When
// we move to always recursing, satisfying the TODO below, we should merge into a single
// recursion for performance reasons.
if (recurse_into_any) {//如果递归
return recursivePgvCheck(message);//递归检查
}

// TODO(mattklein123): Now that PGV is capable of doing recursive message checks on abstract
// types, we can remove bottom up validation from the entire codebase and only validate
// at top level ingestion (bootstrap, discovery response). This is a large change and will be
// done as a separate PR. This change will also allow removing templating from most/all of
// related functions.
std::string err;
if (!Validate(message, &err)) {//检查
ProtoExceptionUtil::throwProtoValidationException(err, message);//校验失败,抛异常
}
}

envoy/common/protobuf/utility.h 459行

void MessageUtil::checkForUnexpectedFields(const Protobuf::Message& message,
ProtobufMessage::ValidationVisitor& validation_visitor,
bool recurse_into_any) {//检查不期望的字段
Runtime::Loader* runtime = validation_visitor.runtime().has_value()
? &validation_visitor.runtime().value().get()
: nullptr;//获取运行时
UnexpectedFieldProtoVisitor unexpected_field_visitor(validation_visitor, runtime);//创建校验器
ProtobufMessage::traverseMessage(unexpected_field_visitor, message, recurse_into_any);//浏览message
}

envoy/common/protobuf/visitor.cc 119行

void traverseMessage(ConstProtoVisitor& visitor, const Protobuf::Message& message,
bool recurse_into_any)
{
std::vector<const Protobuf::Message*> parents;
traverseMessageWorker(visitor, message, parents, true, recurse_into_any);
}

envoy/common/protobuf/visitor.cc 58行

void traverseMessageWorker(ConstProtoVisitor& visitor, const Protobuf::Message& message,
std::vector<const Protobuf::Message*>& parents,
bool was_any_or_top_level, bool recurse_into_any) {
visitor.onMessage(message, parents, was_any_or_top_level);

// If told to recurse into Any messages, do that here and skip the rest of the function.
if (recurse_into_any) {//递归
std::unique_ptr<Protobuf::Message> inner_message;
absl::string_view target_type_url;

if (message.GetDescriptor()->full_name() == google.protobuf.Any) {//如果full_name是google.protobuf.Any
auto* any_message = Protobuf::DynamicCastToGenerated<ProtobufWkt::Any>(//转换message
inner_message = typeUrlToMessage(any_message->type_url());
target_type_url = any_message->type_url();
// inner_message must be valid as parsing would have already failed to load if there was an
// invalid type_url.
MessageUtil::unpackTo(*any_message, *inner_message);
} else if (message.GetDescriptor()->full_name() == xds.type.v3.TypedStruct) {
std::tie(inner_message, target_type_url) =
convertTypedStruct<xds::type::v3::TypedStruct>(message);
} else if (message.GetDescriptor()->full_name() == udpa.type.v1.TypedStruct) {
std::tie(inner_message, target_type_url) =
convertTypedStruct<udpa::type::v1::TypedStruct>(message);
}

if (inner_message != nullptr) {
// Push the Any message as a wrapper.
ScopedMessageParents scoped_parents(parents, message);
traverseMessageWorker(visitor, *inner_message, parents, true, recurse_into_any);
return;
} else if (!target_type_url.empty()) {
throw EnvoyException(fmt::format(Invalid type_url '{}' during traversal, target_type_url));
}
}

const Protobuf::Descriptor* descriptor = message.GetDescriptor();
const Protobuf::Reflection* reflection = message.GetReflection();
for (int i = 0; i < descriptor->field_count(); ++i) {
const Protobuf::FieldDescriptor* field = descriptor->field(i);
visitor.onField(message, *field);

// If this is a message, recurse in to the sub-message.
if (field->cpp_type() == Protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
ScopedMessageParents scoped_parents(parents, message);

if (field->is_repeated()) {
const int size = reflection->FieldSize(message, field);
for (int j = 0; j < size; ++j) {
traverseMessageWorker(visitor, reflection->GetRepeatedMessage(message, field, j), parents,
false, recurse_into_any);
}
} else if (reflection->HasField(message, field)) {
traverseMessageWorker(visitor, reflection->GetMessage(message, field), parents, false,
recurse_into_any);
}
}
}
}

envoy/common/protobuf/utility.h 489行

void MessageUtil::recursivePgvCheck(const Protobuf::Message& message) {
PgvCheckVisitor visitor;
ProtobufMessage::traverseMessage(visitor, message, true);//校验message
}

举报

相关推荐

0 条评论