0
点赞
收藏
分享

微信扫一扫

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

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

热重启

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>(&server);
}

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);//转换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 条评论